<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.23">
<meta name="author" content="Boni García">
<title>WebDriverManager</title>
<style>
@import url(//fonts.googleapis.com/css?family=Titillium+Web:400,700);
@import url(//fonts.googleapis.com/css?family=Noticia+Text:400,400italic);
@import url(//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.0/css/font-awesome.css);
/* Derived from the Riak documentation theme developed by Basho Technologies, Inc. | CC BY 3.0 License | http://docs.basho.org */
/* normalize.css v2.1.1 | MIT License | git.io/normalize */
/* ========================================================================== HTML5 display definitions ========================================================================== */
/** Correct `block` display not defined in IE 8/9. */
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }

/** Correct `inline-block` display not defined in IE 8/9. */
audio, canvas, video { display: inline-block; }

/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
audio:not([controls]) { display: none; height: 0; }

/** Address styling not present in IE 8/9. */
[hidden] { display: none; }

/* ========================================================================== Base ========================================================================== */
/** 1. Prevent system color scheme's background color being used in Firefox, IE, and Opera. 2. Prevent system color scheme's text color being used in Firefox, IE, and Opera. 3. Set default font family to sans-serif. 4. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
html { background: #fff; /* 1 */ color: #000; /* 2 */ font-family: sans-serif; /* 3 */ -ms-text-size-adjust: 100%; /* 4 */ -webkit-text-size-adjust: 100%; /* 4 */ }

/** Remove default margin. */
body { margin: 0; }

/* ========================================================================== Links ========================================================================== */
/** Address `outline` inconsistency between Chrome and other browsers. */
a:focus { outline: thin dotted; }

/** Improve readability when focused and also mouse hovered in all browsers. */
a:active, a:hover { outline: 0; }

/* ========================================================================== Typography ========================================================================== */
/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
h1 { font-size: 2em; margin: 0.67em 0; }

/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
abbr[title] { border-bottom: 1px dotted; }

/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
b, strong { font-weight: bold; }

/** Address styling not present in Safari 5 and Chrome. */
dfn { font-style: italic; }

/** Address differences between Firefox and other browsers. */
hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }

/** Address styling not present in IE 8/9. */
mark { background: #ff0; color: #000; }

/** Correct font family set oddly in Safari 5 and Chrome. */
code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; }

/** Improve readability of pre-formatted text in all browsers. */
pre { white-space: pre-wrap; }

/** Set consistent quote types. */
q { quotes: "\201C" "\201D" "\2018" "\2019"; }

/** Address inconsistent and variable font size in all browsers. */
small { font-size: 80%; }

/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }

sup { top: -0.5em; }

sub { bottom: -0.25em; }

/* ========================================================================== Embedded content ========================================================================== */
/** Remove border when inside `a` element in IE 8/9. */
img { border: 0; }

/** Correct overflow displayed oddly in IE 9. */
svg:not(:root) { overflow: hidden; }

/* ========================================================================== Figures ========================================================================== */
/** Address margin not present in IE 8/9 and Safari 5. */
figure { margin: 0; }

/* ========================================================================== Forms ========================================================================== */
/** Define consistent border, margin, and padding. */
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }

/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
legend { border: 0; /* 1 */ padding: 0; /* 2 */ }

/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ }

/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
button, input { line-height: normal; }

/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
button, select { text-transform: none; }

/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }

/** Re-set default cursor for disabled elements. */
button[disabled], html input[disabled] { cursor: default; }

/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }

/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }

/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }

/** Remove inner padding and border in Firefox 4+. */
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }

/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ }

/* ========================================================================== Tables ========================================================================== */
/** Remove most spacing between table cells. */
table { border-collapse: collapse; border-spacing: 0; }

*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }

html, body { font-size: 100%; }

body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; }

a:hover { cursor: pointer; }

a:focus { outline: none; }

img, object, embed { max-width: 100%; height: auto; }

object, embed { height: 100%; }

img { -ms-interpolation-mode: bicubic; }

#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }

.left { float: left !important; }

.right { float: right !important; }

.text-left { text-align: left !important; }

.text-right { text-align: right !important; }

.text-center { text-align: center !important; }

.text-justify { text-align: justify !important; }

.hide { display: none; }

.antialiased, body { -webkit-font-smoothing: antialiased; }

img { display: inline-block; vertical-align: middle; }

textarea { height: auto; min-height: 50px; }

select { width: 100%; }

p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }

.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #3c3d3f; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }

/* Typography resets */
div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; }

/* Default Link Styles */
a { color: #2984a9; text-decoration: none; line-height: inherit; }
a:hover, a:focus { color: #faa94c; }
a img { border: none; }

/* Default paragraph styles */
p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 0.9375em; text-rendering: geometricPrecision; }
p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; }

/* Default header styles */
h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: "Titillium Web", Verdana, Arial, sans-serif; font-weight: normal; font-style: normal; color: #616366; text-rendering: geometricPrecision; margin-top: 0.5em; margin-bottom: 0.5em; line-height: 1.2125em; }
h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #aeb0b2; line-height: 0; }

h1 { font-size: 2.125em; }

h2 { font-size: 1.6875em; }

h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; }

h4 { font-size: 1.125em; }

h5 { font-size: 1.125em; }

h6 { font-size: 1em; }

hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; }

/* Helpful Typography Defaults */
em, i { font-style: italic; line-height: inherit; }

strong, b { font-weight: bold; line-height: inherit; }

small { font-size: 60%; line-height: inherit; }

code { font-family: monospace, serif; font-weight: normal; color: #515151; }

/* Lists */
ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 0.9375em; list-style-position: outside; font-family: inherit; }

ul, ol { margin-left: 1.5em; }

/* Unordered Lists */
ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ }
ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
ul.square { list-style-type: square; }
ul.circle { list-style-type: circle; }
ul.disc { list-style-type: disc; }
ul.no-bullet { list-style: none; }

/* Ordered Lists */
ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }

/* Definition Lists */
dl dt { margin-bottom: 0.3125em; font-weight: bold; }
dl dd { margin-bottom: 1.25em; }

/* Abbreviations */
abbr, acronym { text-transform: uppercase; font-size: 90%; color: #515151; border-bottom: 1px dotted #dddddd; cursor: help; }

abbr { text-transform: none; }

/* Blockquotes */
blockquote { margin: 0 0 0.9375em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
blockquote cite { display: block; font-size: inherit; color: #484a4c; }
blockquote cite:before { content: "\2014 \0020"; }
blockquote cite a, blockquote cite a:visited { color: #484a4c; }

blockquote, blockquote p { line-height: 1.5; color: #616366; }

/* Microformats */
.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
.vcard li { margin: 0; display: block; }
.vcard .fn { font-weight: bold; font-size: 0.9375em; }

.vevent .summary { font-weight: bold; }
.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }

@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; }
  h1 { font-size: 2.75em; }
  h2 { font-size: 2.3125em; }
  h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
  h4 { font-size: 1.4375em; } }
/* Print styles.  Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com)
*/
.print-only { display: none !important; }

@media print { * { background: transparent !important; color: #000 !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; }
  a, a:visited { text-decoration: underline; }
  a[href]:after { content: " (" attr(href) ")"; }
  abbr[title]:after { content: " (" attr(title) ")"; }
  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
  pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
  thead { display: table-header-group; /* h5bp.com/t */ }
  tr, img { page-break-inside: avoid; }
  img { max-width: 100% !important; }
  @page { margin: 0.5cm; }
  p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; }
  h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; }
  .hide-on-print { display: none !important; }
  .print-only { display: block !important; }
  .hide-for-print { display: none !important; }
  .show-for-print { display: inherit !important; } }
/* Tables */
table { background: #f1f1f1; margin-bottom: 1.25em; border: solid 5px white; }
table thead, table tfoot { background: #dfdfdf; font-weight: bold; }
table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: #222222; text-align: left; }
table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: #222222; }
table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #dfdfdf; }
table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; }

.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
.clearfix:after, .float-group:after { clear: both; }

*:not(pre) > code { font-size: 0.9em; padding: 0 3px; white-space: nowrap; background-color: #dfdfdf; border: 1px solid #c9c9c9; -webkit-border-radius: 4px; border-radius: 4px; text-shadow: none; }

pre, pre > code { line-height: 1.6; color: inherit; font-family: monospace, serif; font-weight: normal; }

kbd.keyseq { color: #848484; }

kbd:not(.keyseq) { display: inline-block; color: #515151; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; }

kbd kbd:first-child { margin-left: 0; }

kbd kbd:last-child { margin-right: 0; }

.menuseq, .menu { color: #373737; }

p a > code:hover { color: #444444; }

#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; }
#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }

#header { margin-bottom: 2.5em; }
#header > h1 { color: #616366; font-family:'Publica Play Regular'; font-weight: normal; border-bottom: 1px solid #dddddd; margin-bottom: -28px; padding-bottom: 32px; padding-top: 10px; background: url('./img/wdm.png') no-repeat left 9.25em top 0em;}
#header span { color: #616366; }
#header #revnumber { text-transform: capitalize; }
#header br { display: none; }
#header br + span { padding-left: 3px; }
#header br + span:before { content: "\2013 \0020"; }
#header br + span.author { padding-left: 0; }
#header br + span.author:before { content: ", "; }

#toc { border-bottom: 1px dashed #cccccc; padding-bottom: 1.25em; background-image: url('./img/toc-bg.jpg'); }
#toc > ul { margin-left: 0.25em; }
#toc ul.sectlevel0 > li > a { font-style: italic; }
#toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
#toc ul { list-style-type: none; }

#toctitle { color: #3c3d3f; }

@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 18.5em; }
  #toc.toc2 { position: fixed; width: 18.5em; left: 0; top: 0; border-right: 1px solid #cccccc; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; }
  #toc.toc2 #toctitle { margin-top: 0; }
  #toc.toc2 ul ul { margin-left: 0; padding-left: 1.25em; }
  #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
  body.toc2.toc-right { padding-left: 0; padding-right: 18em; }
  body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #cccccc; left: auto; right: 0; } }
#content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 4px; border-radius: 4px; }
#content #toc > :first-child { margin-top: 0; }
#content #toc > :last-child { margin-bottom: 0; }
#content #toc a { text-decoration: none; }

#content #toctitle { font-weight: bold; font-family: "Titillium Web", Verdana, Arial, sans-serif; font-size: 1em; padding-left: 0.125em; }

#footer { max-width: 100%; background-color: #515151; padding: 1.25em; }

#footer-text { color: #aeaeae; line-height: 1.35; }

.sect1 { padding-bottom: 1.25em; }

.sect1 + .sect1 { border-top: 1px dashed #cccccc; }

#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; }
#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; display: block; margin-top: 0.05em; }
#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #616366; text-decoration: none; }
#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #555659; }

.imageblock, .literalblock, .listingblock, .verseblock, .videoblock { margin-bottom: 1.25em; }

.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; }

.tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; }

table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }

.admonitionblock > table { border: 0; background: none; width: 100%; }
.admonitionblock > table td.icon { text-align: center; width: 80px; }
.admonitionblock > table td.icon img { max-width: none; }
.admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; }
.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #616366; }
.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }

.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 4px; border-radius: 4px; }
.exampleblock > .content > :first-child { margin-top: 0; }
.exampleblock > .content > :last-child { margin-bottom: 0; }
.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #333333; }
.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; }
.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; }

.exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; }

.sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 4px; border-radius: 4px; }
.sidebarblock > :first-child { margin-top: 0; }
.sidebarblock > :last-child { margin-bottom: 0; }
.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #333333; }
.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; }
.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; }
.sidebarblock > .content > .title { color: #3c3d3f; margin-top: 0; line-height: 1.5; }

.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }

.literalblock > .content pre, .listingblock > .content pre { border-width: 0 0 1px 0; border-style: solid; border-color: #f0f0f0; -webkit-border-radius: 4px; border-radius: 4px; padding: 15px; word-wrap: break-word; }
.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
.literalblock > .content pre > code, .listingblock > .content pre > code { display: block; }
@media only screen { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.64em; } }
@media only screen and (min-width: 768px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.72em; } }
@media only screen and (min-width: 1280px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.8em; } }

.listingblock > .content { position: relative; }

.listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; }

.listingblock:hover code.asciidoc:before { content: "asciidoc"; }
.listingblock:hover code.clojure:before { content: "clojure"; }
.listingblock:hover code.css:before { content: "css"; }
.listingblock:hover code.groovy:before { content: "groovy"; }
.listingblock:hover code.html:before { content: "html"; }
.listingblock:hover code.java:before { content: "java"; }
.listingblock:hover code.javascript:before { content: "javascript"; }
.listingblock:hover code.python:before { content: "python"; }
.listingblock:hover code.ruby:before { content: "ruby"; }
.listingblock:hover code.scss:before { content: "scss"; }
.listingblock:hover code.xml:before { content: "xml"; }
.listingblock:hover code.yaml:before { content: "yaml"; }

.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }

.listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; }

table.pyhltable { border: 0; margin-bottom: 0; }

table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; }

table.pyhltable td.code { padding-left: .75em; padding-right: 0; }

.highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; }

.highlight.pygments .lineno { display: inline-block; margin-right: .25em; }

table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; }

.quoteblock { margin: 0 0 0.9375em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
.quoteblock blockquote { margin: 0 0 0.9375em 0; padding: 0 0 0.5625em 0; border: 0; }
.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
.quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: inherit; color: #484a4c; }
.quoteblock .attribution br { display: none; }
.quoteblock .attribution cite { display: block; margin-bottom: 0.625em; }

table thead th, table tfoot th { font-weight: bold; }

table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 4px; border-radius: 4px; border-top: 5px solid white; border-bottom: 5px solid white; }

table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; }

table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; }

table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child { margin-bottom: 0; }

th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; }

th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; }

th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; }

th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; }

th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; }

th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; }

p.tableblock.header { color: #222222; font-weight: bold; }

td > div.verse { white-space: pre; }

ol { margin-left: 1.75em; }

ul li ol { margin-left: 1.5em; }

dl dd { margin-left: 1.125em; }

dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }

ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.46875em; }

ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }

ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }

ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; }

ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; }

ul.inline { margin: 0 auto 0.46875em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
ul.inline > li > * { display: block; }

.unstyled dl dt { font-weight: normal; font-style: normal; }

ol.arabic { list-style-type: decimal; }

ol.decimal { list-style-type: decimal-leading-zero; }

ol.loweralpha { list-style-type: lower-alpha; }

ol.upperalpha { list-style-type: upper-alpha; }

ol.lowerroman { list-style-type: lower-roman; }

ol.upperroman { list-style-type: upper-roman; }

ol.lowergreek { list-style-type: lower-greek; }

.hdlist > table, .colist > table { border: 0; background: none; }
.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }

td.hdlist1 { padding-right: .8em; font-weight: bold; }

td.hdlist1, td.hdlist2 { vertical-align: top; }

.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }

.colist > table tr > td:first-of-type { padding: 0 .8em; line-height: 1; }
.colist > table tr > td:last-of-type { padding: 0.25em 0; }

.qanda > ol > li > p > em:only-child { color: #247494; }

.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }

.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
.imageblock > .title { margin-bottom: 0; }
.imageblock.thumb, .imageblock.th { border-width: 6px; }
.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }

.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
.image.left { margin-right: 0.625em; }
.image.right { margin-left: 0.625em; }

a.image { text-decoration: none; }

span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; }
span.footnote a, span.footnoteref a { text-decoration: none; }

#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
#footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; }
#footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; }
#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
#footnotes .footnote:last-of-type { margin-bottom: 0; }

#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }

.gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; }
.gist .file-data > table td.line-data { width: 99%; }

div.unbreakable { page-break-inside: avoid; }

.big { font-size: larger; }

.small { font-size: smaller; }

.underline { text-decoration: underline; }

.overline { text-decoration: overline; }

.line-through { text-decoration: line-through; }

.aqua { color: #00bfbf; }

.aqua-background { background-color: #00fafa; }

.black { color: black; }

.black-background { background-color: black; }

.blue { color: #0000bf; }

.blue-background { background-color: #0000fa; }

.fuchsia { color: #bf00bf; }

.fuchsia-background { background-color: #fa00fa; }

.gray { color: #606060; }

.gray-background { background-color: #7d7d7d; }

.green { color: #006000; }

.green-background { background-color: #007d00; }

.lime { color: #00bf00; }

.lime-background { background-color: #00fa00; }

.maroon { color: #600000; }

.maroon-background { background-color: #7d0000; }

.navy { color: #000060; }

.navy-background { background-color: #00007d; }

.olive { color: #606000; }

.olive-background { background-color: #7d7d00; }

.purple { color: #600060; }

.purple-background { background-color: #7d007d; }

.red { color: #bf0000; }

.red-background { background-color: #fa0000; }

.silver { color: #909090; }

.silver-background { background-color: #bcbcbc; }

.teal { color: #006060; }

.teal-background { background-color: #007d7d; }

.white { color: #bfbfbf; }

.white-background { background-color: #fafafa; }

.yellow { color: #bfbf00; }

.yellow-background { background-color: #fafa00; }

span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

.admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 0 0 8px white, 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #2984a9; color: #1f637f; }
.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }

.conum { display: inline-block; color: white !important; background-color: #515151; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; }
.conum * { color: white !important; }
.conum + b { display: none; }
.conum:after { content: attr(data-value); }
.conum:not([data-value]):empty { display: none; }

::selection { background-color: #fcc07f; color: #fff; }

.literalblock > .content > pre, .listingblock > .content > pre { -webkit-box-shadow: inset 0 1px 4px #aeb9b6; box-shadow: inset 0 1px 4px #aeb9b6; -webkit-border-radius: 5px; border-radius: 5px; }

#content ul > li { list-style-type: square; }

/* p > em { font-family: 'Noticia Text', serif; font-weight: 400; font-size: 95%; } */

.admonitionblock > table { width: 100%; background-image: url('./img/info-bg.jpg'); border-collapse: separate; border-spacing: 0; -webkit-border-radius: 5px; border-radius: 5px; border: 1px solid #9EC6DF; }
.admonitionblock > table td.icon { padding: 15px; }
.admonitionblock > table td.icon .icon-tip:before { text-shadow: 0 0 20px white, 1px 1px 2px rgba(155, 155, 0, 0.8); }
.admonitionblock > table td.content { font-family: 'Noticia Text', italic; font-size: 90%; font-style: italic; border: 0; padding: 15px; }

.admonitionblock .literalblock > .content > pre, .admonitionblock .listingblock > .content > pre { background-image: url('./img/info-bg.jpg'); }

.exampleblock > .content { background-color: transparent; border-color: #c9c9c9; }

.sidebarblock > .content > .title { font-size: 1.25em; font-weight: bold; }
.sidebarblock > .content code { color: #333; }
.sidebarblock > .content a { color: #60B5D8; }
.sidebarblock > .content a:hover { color: #FAA94C; }
.sidebarblock > .content .admonitionblock p, .sidebarblock > .content .admonitionblock ul { text-shadow: none; color: #616366; }

table.tableblock.grid-all { -webkit-border-radius: 0; border-radius: 0; -webkit-box-shadow: 0 1px 3px #999999; box-shadow: 0 1px 3px #999999; }

#footer { background-image: url('./img/footer-bg.jpg'); padding: 25px 0; }

#footer-text { color: #fff; text-shadow: 1px 1px 1px #333; font-size: 80%; text-align: center; }

@font-face {
    font-family: 'Publica Play Regular';
    font-style: normal;
    font-weight: normal;
    src: url('./css/PublicaPlay-Regular.woff') format('woff');
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
/*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */
pre.CodeRay{background:#f7f7f8}
.CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.CodeRay span.line-numbers{display:inline-block;margin-right:.75em}
.CodeRay .line-numbers strong{color:#000}
table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.CodeRay td{vertical-align:top;line-height:inherit}
table.CodeRay td.line-numbers{text-align:right}
table.CodeRay td.code{padding:0 0 0 .75em}
.CodeRay .debug{color:#fff!important;background:navy!important}
.CodeRay .annotation{color:#007}
.CodeRay .attribute-name{color:navy}
.CodeRay .attribute-value{color:#700}
.CodeRay .binary{color:#509}
.CodeRay .comment{color:#998;font-style:italic}
.CodeRay .char{color:#04d}
.CodeRay .char .content{color:#04d}
.CodeRay .char .delimiter{color:#039}
.CodeRay .class{color:#458;font-weight:bold}
.CodeRay .complex{color:#a08}
.CodeRay .constant,.CodeRay .predefined-constant{color:teal}
.CodeRay .color{color:#099}
.CodeRay .class-variable{color:#369}
.CodeRay .decorator{color:#b0b}
.CodeRay .definition{color:#099}
.CodeRay .delimiter{color:#000}
.CodeRay .doc{color:#970}
.CodeRay .doctype{color:#34b}
.CodeRay .doc-string{color:#d42}
.CodeRay .escape{color:#666}
.CodeRay .entity{color:#800}
.CodeRay .error{color:#808}
.CodeRay .exception{color:inherit}
.CodeRay .filename{color:#099}
.CodeRay .function{color:#900;font-weight:bold}
.CodeRay .global-variable{color:teal}
.CodeRay .hex{color:#058}
.CodeRay .integer,.CodeRay .float{color:#099}
.CodeRay .include{color:#555}
.CodeRay .inline{color:#000}
.CodeRay .inline .inline{background:#ccc}
.CodeRay .inline .inline .inline{background:#bbb}
.CodeRay .inline .inline-delimiter{color:#d14}
.CodeRay .inline-delimiter{color:#d14}
.CodeRay .important{color:#555;font-weight:bold}
.CodeRay .interpreted{color:#b2b}
.CodeRay .instance-variable{color:teal}
.CodeRay .label{color:#970}
.CodeRay .local-variable{color:#963}
.CodeRay .octal{color:#40e}
.CodeRay .predefined{color:#369}
.CodeRay .preprocessor{color:#579}
.CodeRay .pseudo-class{color:#555}
.CodeRay .directive{font-weight:bold}
.CodeRay .type{font-weight:bold}
.CodeRay .predefined-type{color:inherit}
.CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold}
.CodeRay .key{color:#808}
.CodeRay .key .delimiter{color:#606}
.CodeRay .key .char{color:#80f}
.CodeRay .value{color:#088}
.CodeRay .regexp .delimiter{color:#808}
.CodeRay .regexp .content{color:#808}
.CodeRay .regexp .modifier{color:#808}
.CodeRay .regexp .char{color:#d14}
.CodeRay .regexp .function{color:#404;font-weight:bold}
.CodeRay .string{color:#d20}
.CodeRay .string .string .string{background:#ffd0d0}
.CodeRay .string .content{color:#d14}
.CodeRay .string .char{color:#d14}
.CodeRay .string .delimiter{color:#d14}
.CodeRay .shell{color:#d14}
.CodeRay .shell .delimiter{color:#d14}
.CodeRay .symbol{color:#990073}
.CodeRay .symbol .content{color:#a60}
.CodeRay .symbol .delimiter{color:#630}
.CodeRay .tag{color:teal}
.CodeRay .tag-special{color:#d70}
.CodeRay .variable{color:#036}
.CodeRay .insert{background:#afa}
.CodeRay .delete{background:#faa}
.CodeRay .change{color:#aaf;background:#007}
.CodeRay .head{color:#f8f;background:#505}
.CodeRay .insert .insert{color:#080}
.CodeRay .delete .delete{color:#800}
.CodeRay .change .change{color:#66f}
.CodeRay .head .head{color:#f4f}
</style>
<link rel="icon" type="image/png" href="//bonigarcia.dev/img/webdrivermanager.png">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-92JGC4402E"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-92JGC4402E');
</script>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>WebDriverManager</h1>
<div class="details">
<span id="author" class="author">Boni García</span><br>
<span id="revdate">Version 6.3.3</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#motivation">1. Motivation</a>
<ul class="sectlevel2">
<li><a href="#webdrivermanager-and-selenium-manager">1.1. WebDriverManager and Selenium Manager</a></li>
</ul>
</li>
<li><a href="#setup">2. Setup</a></li>
<li><a href="#features">3. Features</a>
<ul class="sectlevel2">
<li><a href="#driver-management">3.1. Driver Management</a>
<ul class="sectlevel3">
<li><a href="#resolution-algorithm">3.1.1. Resolution Algorithm</a></li>
<li><a href="#generic-manager">3.1.2. Generic Manager</a></li>
<li><a href="#advanced-settings">3.1.3. Advanced Settings</a></li>
</ul>
</li>
<li><a href="#browser-finder">3.2. Browser Finder</a></li>
<li><a href="#webdriver-builder">3.3. WebDriver Builder</a></li>
<li><a href="#browsers-in-docker">3.4. Browsers in Docker</a>
<ul class="sectlevel3">
<li><a href="#browser-versions">3.4.1. Browser Versions</a></li>
<li><a href="#remote-desktop">3.4.2. Remote Desktop</a></li>
<li><a href="#video-recording">3.4.3. Video Recording</a></li>
<li><a href="#advanced-settings-2">3.4.4. Advanced Settings</a></li>
</ul>
</li>
<li><a href="#browsers-monitoring">3.5. Browsers Monitoring</a>
<ul class="sectlevel3">
<li><a href="#console-log-gathering">3.5.1. Console Log Gathering</a></li>
<li><a href="#console-log-displaying">3.5.2. Console Log Displaying</a></li>
<li><a href="#video-recording-2">3.5.3. Video Recording</a></li>
<li><a href="#disabling-csp">3.5.4. Disabling CSP</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#other-usages">4. Other Usages</a>
<ul class="sectlevel2">
<li><a href="#webdrivermanager-cli">4.1. WebDriverManager CLI</a></li>
<li><a href="#webdrivermanager-server">4.2. WebDriverManager Server</a></li>
<li><a href="#webdrivermanager-agent">4.3. WebDriverManager Agent</a></li>
<li><a href="#selenium-jupiter">4.4. Selenium-Jupiter</a></li>
<li><a href="#selenium-grid">4.5. Selenium Grid</a></li>
<li><a href="#appium">4.6. Appium</a></li>
</ul>
</li>
<li><a href="#examples">5. Examples</a></li>
<li><a href="#advanced-configuration">6. Advanced Configuration</a></li>
<li><a href="#known-issues">7. Known Issues</a>
<ul class="sectlevel2">
<li><a href="#http-response-code-403">7.1. HTTP response code 403</a></li>
<li><a href="#testing-localhost-in-docker">7.2. Testing localhost in Docker</a></li>
<li><a href="#chrome-92-94-in-docker">7.3. Chrome 92-94 in Docker</a></li>
<li><a href="#support-for-chromedriver-115">7.4. Support for chromedriver 115+</a></li>
<li><a href="#connectivity-issues">7.5. Connectivity issues</a></li>
</ul>
</li>
<li><a href="#troubleshooting">8. Troubleshooting</a></li>
<li><a href="#community">9. Community</a></li>
<li><a href="#support">10. Support</a></li>
<li><a href="#further-documentation">11. Further Documentation</a></li>
<li><a href="#about">12. About</a></li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="motivation"><a class="anchor" href="#motivation"></a>1. Motivation</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.selenium.dev/documentation/webdriver/">Selenium WebDriver</a> is a library that allows controlling web browsers programmatically. It provides a cross-browser API that can be used to drive web browsers (e.g., Chrome, Edge, or Firefox, among others) using different programming languages (e.g., Java, JavaScript, Python, C#, or Ruby). The primary use of Selenium WebDriver is implementing automated tests for web applications.</p>
</div>
<div class="paragraph">
<p>Selenium WebDriver carries out the automation using the native support of each browser. For this reason, we need to place a binary file called <em>driver</em> between the test using the Selenium WebDriver API and the browser to be controlled. Examples of drivers for major web browsers nowadays are <a href="https://chromedriver.chromium.org/">chromedriver</a> (for Chrome), <a href="https://github.com/mozilla/geckodriver/">geckodriver</a> (for Firefox), or <a href="https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/">msedgedriver</a> (for Edge). As you can see in the following picture, the communication between the WebDriver API and the driver binary is done using a standard protocol called <a href="https://www.w3.org/TR/webdriver/">W3C WebDriver</a> (formerly the so-called <em>JSON Wire Protocol</em>). Then, the communication between the driver and the browser is done using the native capabilities of each browser.</p>
</div>
<div id="webdriver_arch" class="imageblock thumb">
<div class="content">
<img src="img/selenium-webdriver-architecture.png" alt="selenium webdriver architecture">
</div>
<div class="title">Figure 1. Selenium WebDriver Architecture</div>
</div>
<style>
#carbonads * {
	margin: initial;
	padding: initial;
}

#carbonads {
	font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
		Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial,
		sans-serif;
}

#carbonads {
	display: flex;
	max-width: 330px;
	background-color: hsl(0, 0%, 98%);
	box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
	z-index: 100;
}

#carbonads a {
	color: inherit;
	text-decoration: none;
}

#carbonads a:hover {
	color: inherit;
}

#carbonads span {
	position: relative;
	display: block;
	overflow: hidden;
}

#carbonads .carbon-wrap {
	display: flex;
}

#carbonads .carbon-img {
	display: block;
	margin: 0;
	line-height: 1;
}

#carbonads .carbon-img img {
	display: block;
}

#carbonads .carbon-text {
	font-size: 13px;
	padding: 10px;
	margin-bottom: 16px;
	line-height: 1.5;
	text-align: left;
}

#carbonads .carbon-poweredby {
	display: block;
	padding: 6px 8px;
	background: #f1f1f2;
	text-align: center;
	text-transform: uppercase;
	letter-spacing: 0.5px;
	font-weight: 600;
	font-size: 8px;
	line-height: 1;
	border-top-left-radius: 3px;
	position: absolute;
	bottom: 0;
	right: 0;
}
</style>

<script async type="text/javascript"
	src="//cdn.carbonads.com/carbon.js?serve=CEADT23M&placement=bonigarciadev"
	id="_carbonads_js"></script>
<br>
<div class="paragraph">
<p>From a practical point of view, we need to make a <em>driver management process</em> to use Selenium WebDriver. This process consists on:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Download. Drivers are platform-specific binary files. To download the proper driver, we have to identify the driver type we need (e.g., chromedriver if we want to use Chrome), the operating system (typically, Windows, Linux, or Mac OS), the architecture (typically, 32 or 64 bits), and very important, the driver version. Concerning the version, each driver release is usually compatible with a given browser version(s). For this reason, we need to discover the correct driver version for a specific browser release (typically reading the driver documentation or release notes).</p>
</li>
<li>
<p>Setup. Once we have downloaded the driver to our computer, we need to provide a way to locate this driver from our Selenium WebDriver tests. In Java, this setup can be done in two different ways. First, we can add the driver location to our <code>PATH</code> environmental variable. Second, we can use <em>Java system properties</em> to export the driver path. Each driver path should be identified using a given system property, as follows:</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">System</span>.setProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">webdriver.chrome.driver</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/path/to/chromedriver</span><span class="delimiter">&quot;</span></span>);
<span class="predefined-type">System</span>.setProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">webdriver.gecko.driver</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/path/to/geckodriver</span><span class="delimiter">&quot;</span></span>);
<span class="predefined-type">System</span>.setProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">webdriver.edge.driver</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/path/to/msedgedriver</span><span class="delimiter">&quot;</span></span>);
<span class="predefined-type">System</span>.setProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">webdriver.opera.driver</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/path/to/operadriver</span><span class="delimiter">&quot;</span></span>);
<span class="predefined-type">System</span>.setProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">webdriver.ie.driver</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">C:/path/to/IEDriverServer.exe</span><span class="delimiter">&quot;</span></span>);</code></pre>
</div>
</div>
</li>
<li>
<p>Maintenance. Last but not least, we need to warranty the compatibility between driver and browser in time. This step is relevant since modern browsers automatically upgrade themselves (i.e., they are <em>evergreen</em> browsers), and for this reason, the compatibility driver-browser is not warranted in the long run. For instance, when a WebDriver test using Chrome faces a driver incompatibility, it reports the following error message: "<em>this version of chromedriver only supports chrome version N</em>." As you can see in <a href="https://stackoverflow.com/search?q=this+version+of+chromedriver+only+supports+Chrome+version">StackOverflow</a>, this is a recurrent problem for manually managed drivers (chromedriver in this case).</p>
</li>
</ol>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">What is WebDriverManager?</div>
<div class="paragraph">
<p><a href="https://github.com/bonigarcia/webdrivermanager">WebDriverManager</a> is an open-source Java library that carries out the management (i.e., download, setup, and maintenance) of the drivers required by Selenium WebDriver (e.g., chromedriver, geckodriver, msedgedriver, etc.) in a fully automated manner. In addition, as of version 5, WebDriverManager provides other relevant features, such as the capability to <a href="#browser-finder">discover browsers</a> installed in the local system, <a href="#webdriver-builder">building WebDriver objects</a> (such as <code>ChromeDriver</code>, <code>FirefoxDriver</code>, <code>EdgeDriver</code>, etc.), running <a href="#browsers-in-docker">browsers in Docker containers</a> seamlessly, and <a href="#browsers-monitoring">monitoring capabilities</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="webdrivermanager-and-selenium-manager"><a class="anchor" href="#webdrivermanager-and-selenium-manager"></a>1.1. WebDriverManager and Selenium Manager</h3>
<div class="paragraph">
<p>Maybe you have heard (and if not, you should) about <a href="https://www.selenium.dev/documentation/selenium_manager/">Selenium Manager</a>. Selenium Manager is the official driver manager of the Selenium project, and it is shipped out of the box with every Selenium release. You might have some doubts about this:</p>
</div>
<div class="paragraph">
<p><em>Is Selenium Manager a replacement for WebDriverManger?</em> For the use case of automated driver management, yes. In other words, if you use WebDriverManager only for driver management, you can safely switch to Selenium Manager.</p>
</div>
<div class="paragraph">
<p><em>What are the differences between WebDriverManager and Selenium Manager?</em> Both projects provide automated driver management (for chromedriver, geckodriver, etc.). But, WebDriverManager provides several features not available in Selenium Manager (e.g., self-managed browsers in Docker containers or custom monitoring features). On the other side, Selenium Manager provides automated browser management (e.g., based on <a href="https://googlechromelabs.github.io/chrome-for-testing/">Chrome for Testing</a>).</p>
</div>
<div class="paragraph">
<p><em>Then, should I move to Selenium Manager?</em> It depends. If you use some custom feature of WebDriverManager, you can continue using it. If you use WebDriverManager only for automated management, you can switch to Selenium Manager. However, if you cannot bump to Java 11 (which is the <a href="https://www.selenium.dev/blog/2023/java-8-support/">minimum Java version for the latest versions of Selenium by September 2023</a>), WebDriverManager can still be your library for driver management, since WebDriverManager will continue supporting Java 8 (at least for some time more).</p>
</div>
<div class="paragraph">
<p><em>Will the WebDriverManger development stop?</em> WebDriverManger might still be helpful, so its development and maintenance continue.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="setup"><a class="anchor" href="#setup"></a>2. Setup</h2>
<div class="sectionbody">
<div class="paragraph">
<p>WebDriverManager is primarily used as a Java dependency (although <a href="#other-usages">other usages</a> are also possible). We typically use a <em>build tool</em> (such as <a href="https://maven.apache.org/">Maven</a> or <a href="https://gradle.org/">Gradle</a>) to resolve the WebDriverManager dependency. In Maven, it can be done as follows (notice that it is declared using the <code>test</code> scope, since it is typically used in tests classes):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.github.bonigarcia<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>webdrivermanager<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>6.3.3<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;scope&gt;</span>test<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>In the case of a Gradle project, we can declare WebDriverManager as follows (again, for tests):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="json"><span class="error">d</span><span class="error">e</span><span class="error">p</span><span class="error">e</span><span class="error">n</span><span class="error">d</span><span class="error">e</span><span class="error">n</span><span class="error">c</span><span class="error">i</span><span class="error">e</span><span class="error">s</span> {
    <span class="error">t</span><span class="error">e</span><span class="error">s</span><span class="error">t</span><span class="error">I</span><span class="error">m</span><span class="error">p</span><span class="error">l</span><span class="error">e</span><span class="error">m</span><span class="error">e</span><span class="error">n</span><span class="error">t</span><span class="error">a</span><span class="error">t</span><span class="error">i</span><span class="error">o</span><span class="error">n</span><span class="error">(</span><span class="string"><span class="delimiter">&quot;</span><span class="content">io.github.bonigarcia:webdrivermanager:6.3.3</span><span class="delimiter">&quot;</span></span><span class="error">)</span>
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="features"><a class="anchor" href="#features"></a>3. Features</h2>
<div class="sectionbody">
<div class="paragraph">
<p>WebDriverManager provides a fluent API available using the class <code>WebDriverManager</code> (package <code>io.github.bonigarcia.wdm</code>). This class provides a group of static methods to create <em>managers</em>, i.e., objects devoted to providing automated driver management and other features.</p>
</div>
<div class="sect2">
<h3 id="driver-management"><a class="anchor" href="#driver-management"></a>3.1. Driver Management</h3>
<div class="paragraph">
<p>The primary use of WebDriverManager is the automation of driver management. To use this feature, you need to select a given manager in the WebDriverMager API (e.g., <code>chromedriver()</code> for Chrome) and invoke the method <code>setup()</code>. The following example shows a test case using <a href="https://junit.org/junit5/">JUnit 5</a>, Selenium WebDriver, WebDriverManager, and <a href="https://joel-costigliola.github.io/assertj/">AssertJ</a> (for fluent assertions). In this test, we invoke WebDriverManager in the setup method for all tests (<code>@BeforeAll</code>). This way, the required driver (chromedriver) will be available for all the WebDriver tests using Chrome in this class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeAll</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.chrome.ChromeDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">ChromeTest</span> {

    WebDriver driver;

    <span class="annotation">@BeforeAll</span>
    <span class="directive">static</span> <span class="type">void</span> setupClass() {
        WebDriverManager.chromedriver().setup();
    }

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = <span class="keyword">new</span> ChromeDriver();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        <span class="comment">// Exercise</span>
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">String</span> title = driver.getTitle();

        <span class="comment">// Verify</span>
        assertThat(title).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>WebDriverManager provides a set of <em>managers</em> for Chrome, Firefox, Edge, Opera, Chromium, and Internet Explorer. The basic use of these managers is the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">WebDriverManager.chromedriver().setup();
WebDriverManager.firefoxdriver().setup();
WebDriverManager.edgedriver().setup();
WebDriverManager.operadriver().setup();
WebDriverManager.chromiumdriver().setup()
WebDriverManager.iedriver().setup();</code></pre>
</div>
</div>
<div class="paragraph">
<p>As of version 5, WebDriverManager also provides a manager for Safari (called <code>safaridriver()</code>). The case of the Safari browser is particular since this browser does not require to manage its driver to work with Selenium WebDriver (in other words, the Safari driver is built-in within the browser). Nevertheless, WebDriverManager provides this manager to be used in the WebDriver builder (especially with <a href="#browsers-in-docker">Docker</a>).</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Although not mandatory, it is highly recommended to use a logger library to trace your application and tests. In the case of WebDriverManager, you will see the relevant steps of the driver management following its traces. See for example the following <a href="https://www.stubbornjava.com/posts/logging-in-java-with-slf4j-and-logback)">tutorial</a> to use <a href="http://www.slf4j.org/">SLF4J </a> and <a href="http://logback.qos.ch/">Logback</a>. Also, you can see an example of a WebDriverManager test using logging <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/firefox/FirefoxTest.java">here</a> (this example uses this <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/logback.xml">configuration file</a>).
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="resolution-algorithm"><a class="anchor" href="#resolution-algorithm"></a>3.1.1. Resolution Algorithm</h4>
<div class="paragraph">
<p>WebDriverManager executes a <em>resolution algorithm</em> when calling to <code>setup()</code> in a given manager. You can find all its internal details in the paper <a href="https://link.springer.com/article/10.1007/s10664-021-09975-3">Automated driver management for Selenium WebDriver</a>, published in the Springer Journal of Empirical Software Engineering in 2021. The most relevant parts of this algorithm are the following:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>WebDriverManager tries to find the browser version. To this aim, WebDriverManager uses internally a knowledge database called <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a>. This database is a collection of shell commands used to discover the version of a given browser in the different operating systems (e.g., <code>google-chrome --version</code> for Chrome in Linux). As of WebDriverManager 6.0.0, you can specify the browser binary path (using the method <code>browserBinary()</code> ) that WebDriverManager will use for browser version discovery.</p>
</li>
<li>
<p>Using the browser version, WebDriverManager tries to find the proper driver version. This process is different for each browser. For chromedriver, the <a href="https://github.com/GoogleChromeLabs/chrome-for-testing">Chrome for Testing (CfT) endpoints</a> are used. For geckodriver, the <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/trunk/common/geckodriver/geckodriver-support.json">Firefox mapping maintained by the Selenium project</a> (which is based on the <a href="https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.html">official geckodriver support</a>). For msedgedriver, the <a href="https://msedgedriver.azureedge.net/">Edge metadata</a> is used.</p>
</li>
<li>
<p>Once the driver version is discovered, WebDriverManager downloads this driver to a local cache (located at <code>~/.cache/selenium</code> by default). These drivers are reused in subsequent calls.</p>
</li>
<li>
<p>Finally, WebDriverManager exports the driver path using Java system properties (e.g., <code>webdriver.chrome.driver</code> in the case of the Chrome manager).</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>This process automated the first two stages of the driver management previously introduced, i.e., download and setup. To support the third stage (i.e., maintenance), WebDriverManager implements <em>resolution cache</em>. This cache (called by default <code>resolution.properties</code> and stored in the root of the driver cache) is a file that stores the relationship between the resolved driver and browser versions. This relationship is valid during a given <em>time-to-live</em> (TTL). The default value for this TTL is 1 hour for browsers and 1 day for drivers. In other words, the discovered browser version is valid for 1 hour, and the driver version is considered correct for 1 day. This mechanism improves the performance dramatically since the second (and following) calls to the resolution algorithm for the same browser are resolved using only local resources (i.e., without using the shell nor requesting external services).</p>
</div>
</div>
<div class="sect3">
<h4 id="generic-manager"><a class="anchor" href="#generic-manager"></a>3.1.2. Generic Manager</h4>
<div class="paragraph">
<p>WebDriverManager provides a <em>generic manager</em>, i.e., a manager that can be parameterized to act as a specific manager (for Chrome, Firefox, etc.). Using this feature, you can create a manager using the method <code>getInstance()</code> of the WebDriverManager API. The method can be invoked using the following options:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>getInstance(Class&lt;? extends WebDriver&gt; webDriverClass)</code>: Where <code>webDriverClass</code> is a class of the Selenium WebDriver standard hierarchy, such as <code>ChromeDriver.class</code>, <code>FirefoxDriver.class</code>, etc.</p>
</li>
<li>
<p><code>getInstance(DriverManagerType driverManagerType)</code>: Where <code>driverManagerType</code> is an <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/java/io/github/bonigarcia/wdm/config/DriverManagerType.java">enumeration</a> provided by WebDriverManager to identify the available managers.</p>
</li>
<li>
<p><code>getInstance(String browserName)</code>: Where <code>browserName</code> is the usual browser name as <code>String</code> (i.e., <code>"Chrome"</code>, <code>"Firefox"</code>, <code>"Edge"</code>, <code>"Opera"</code>, <code>"Chromium"</code>, <code>"Safari"</code>, or <code>"IExplorer"</code>).</p>
</li>
<li>
<p><code>getInstance()</code>: If no parameter is specified, the configuration key <code>wdm.defaultBrowser</code> is used to select the manager (Chrome by default). See the <a href="#advanced-configuration">advanced configuration</a> section for further information about the configuration capabilities of WebDriverManager.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The following example shows a <a href="https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests">JUnit 5 parameterized test</a> in which the test is repeated twice (using the classes <code>ChromeDriver.class</code> and <code>FirefoxDriver.class</code> as test parameters). As you can see, WebDriverManager uses this parameter to instantiate the proper manager and create the <code>WebDriver</code> instance (see <a href="#webdriver-builder">WebDriver Builder</a> section for more information about this feature).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.params.ParameterizedTest</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.params.provider.ValueSource</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.chrome.ChromeDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.firefox.FirefoxDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">GenericTest</span> {

    WebDriver driver;

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@ParameterizedTest</span>
    <span class="annotation">@ValueSource</span>(classes = { ChromeDriver.class, FirefoxDriver.class })
    <span class="type">void</span> test(<span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> WebDriver&gt; webDriverClass) {
        <span class="comment">// Driver management and WebDriver instantiation</span>
        driver = WebDriverManager.getInstance(webDriverClass).create();

        <span class="comment">// Exercise</span>
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">String</span> title = driver.getTitle();

        <span class="comment">// Verify</span>
        assertThat(title).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="advanced-settings"><a class="anchor" href="#advanced-settings"></a>3.1.3. Advanced Settings</h4>
<div class="paragraph">
<p>The driver management provided by WebDriverManager can be tuned in many different ways. For example, the WebDriverManager API allows configuring the driver (or browser) version to be resolved, the location of the cache path, the operating system, the architecture, the proxy settings (for the network connection), the TTL, or cleaning the driver and resolution cache, among many other attributes. See the <a href="#advanced-configuration">advanced configuration</a> settings for specific details about it. In addition, there are several WebDriverManager API methods specific to driver management, namely:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>getDownloadedDriverPath()</code>: Used to find out the path of the resolved driver in the current instance of WebDriverManager.</p>
</li>
<li>
<p><code>getDownloadedDriverVersion()</code>: Used to find out the version of the driver resolved in the current instance of WebDriverManager.</p>
</li>
<li>
<p><code>getResolvedBrowserVersion()</code>: Used to find out the major browser version discovered in the current instance of WebDriverManager.</p>
</li>
<li>
<p><code>getDriverVersions()</code>: Used to find the list of available driver versions in a given manager.</p>
</li>
<li>
<p><code>getDriverManagerType()</code>: Used to get the driver manager type (and <code>enum</code>) of a given manager.</p>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
Each manager was a singleton object in older WebDriverManager releases (e.g., 4.x), while in version 5, a  new manager instance is created each time. Therefore, the usage of <code>getDownloadedDriverPath()</code> and <code>getDownloadedDriverVersion()</code> can be different in WebDriverManager 5 (i.e., these methods need to be invoked using a WebDriverManager instance previously created).
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="browser-finder"><a class="anchor" href="#browser-finder"></a>3.2. Browser Finder</h3>
<div class="paragraph">
<p>As of version 5, WebDriverManager allows detecting if a given browser is installed or not in the local system. To this aim, each manager provides the method <code>getBrowserPath()</code>. This method returns an <code>Optional&lt;Path&gt;</code>, which is empty if a given browser is not installed in the system or the browser path (within the optional object) when detected.</p>
</div>
<div class="paragraph">
<p>The following example shows an example using this feature.
In this test, the optional browser path is used to disable conditionally (i.e., skip) the test using an <a href="https://assertj.github.io/doc/#assertj-core-assumptions">AssertJ assumption</a> (although other built-in assumptions available in JUnit 5 or other unit testing frameworks are also possible). This test should be executed in a Mac OS system (which should have Safari out of the box), but it should be skipped in any other operating system.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assumptions.assumeThat</span>;

<span class="keyword">import</span> <span class="include">java.nio.file.Path</span>;
<span class="keyword">import</span> <span class="include">java.util.Optional</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeAll</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.safari.SafariDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">SafariTest</span> {

    WebDriver driver;

    <span class="annotation">@BeforeAll</span>
    <span class="directive">static</span> <span class="type">void</span> setupClass() {
        Optional&lt;Path&gt; browserPath = WebDriverManager.safaridriver()
                .getBrowserPath();
        assumeThat(browserPath).isPresent();
    }

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = <span class="keyword">new</span> SafariDriver();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        <span class="comment">// Exercise</span>
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">String</span> title = driver.getTitle();

        <span class="comment">// Verify</span>
        assertThat(title).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Internally, WebDriverManager uses the content of the <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a> to detect the possible browser paths in different operating systems.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="webdriver-builder"><a class="anchor" href="#webdriver-builder"></a>3.3. WebDriver Builder</h3>
<div class="paragraph">
<p>As of version 5, WebDriverManager allows instantiating <code>WebDriver</code> objects (e.g. <code>ChromeDriver</code>, <code>FirefoxDriver</code>, etc.) using the WebDriverManager API. This feature is available using each manager&#8217;s method <code>create()</code>. The following example shows a test using this feature. Notice that the WebDriverManager call to the <code>setup()</code> method is not required when using this feature since the driver management is done internally by WebDriverManager. WebDriverManager provides the method <code>quit()</code> to close the created <code>WebDriver</code> instances gracefully to complement this feature.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">ChromeCreateTest</span> {

    WebDriver driver;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = WebDriverManager.chromedriver().create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
When using this feature, WebDriverManager stores internally a reference to the <code>WebDriver</code> objects created. In addition, a shutdown hook watches these objects correctly released before shutting down the JVM. You can play with this feature by removing the <code>teardown</code> method of the example before.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The WebDriverManager API provides different methods to enhance the creation of <code>WebDriver</code> objects, such as:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Integer parameter in the method <code>create()</code>. This option is used to create a list of <code>WebDriver</code> objects instead of a single instance. See <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/create/ChromeListCreateTest.java">example</a>.</p>
</li>
<li>
<p>Method <code>capabilities()</code>: To specify WebDriver <code>Capabilities</code> (see <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/create/ChromeCapabilitiesCreateTest.java">example</a>).</p>
</li>
<li>
<p>Method <code>remoteAddress()</code>: To specify the remote URL in the <code>RemoteWebDriver</code> instance, typically when using a Selenium Server or a cloud provider (such as <a href="https://saucelabs.com/">Sauce Labs</a>, <a href="https://www.lambdatest.com/">LambdaTest</a>, etc.). This method is equivalent to the configuration key <code>wdm.remoteAddress</code> (see <a href="#advanced-configuration">configuration</a> section). The following example shows a test using this method. This test starts <a href="https://www.selenium.dev/documentation/en/grid/">Selenium Grid</a> in standalone mode before the test. To that, WebDriverManager is also used (since the browser controlled by Selenium Grid also requires a driver):</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeAll</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.grid.Main</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">ChromeRemoteTest</span> {

    WebDriver driver;

    <span class="annotation">@BeforeAll</span>
    <span class="directive">static</span> <span class="type">void</span> setupClass() {
        <span class="comment">// Resolve driver for Selenium Grid</span>
        WebDriverManager.chromedriver().setup();

        <span class="comment">// Start Selenium Grid in standalone mode</span>
        Main.main(<span class="keyword">new</span> <span class="predefined-type">String</span><span class="type">[]</span> { <span class="string"><span class="delimiter">&quot;</span><span class="content">standalone</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">--port</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">4445</span><span class="delimiter">&quot;</span></span> });
    }

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = WebDriverManager.chromedriver()
                .remoteAddress(<span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:4445/wd/hub</span><span class="delimiter">&quot;</span></span>).create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="browsers-in-docker"><a class="anchor" href="#browsers-in-docker"></a>3.4. Browsers in Docker</h3>
<div class="paragraph">
<p>Another relevant new feature available in WebDriverManager 5 is the ability to create browsers in <a href="https://www.docker.com/">Docker</a> containers out of the box. The requirement to use this feature is to have installed a <a href="https://docs.docker.com/engine/">Docker Engine</a> in the machine running the tests. To use it, we need to invoke the method <code>browserInDocker()</code> in conjunction with <code>create()</code> of a given manager. This way, WebDriverManager pulls the image from <a href="https://hub.docker.com/">Docker Hub</a>, starts the container, and instantiates the <code>WebDriver</code> object to use it. The following test shows a simple example using Chrome in Docker:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DockerChromeTest</span> {

    WebDriver driver;

    WebDriverManager wdm = WebDriverManager.chromedriver().browserInDocker();

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        wdm.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
When using browsers in Docker containers, the call to the method <code>quit()</code> of the WebDriverManager API allows the disposal of the browser container(s).
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>As of version 6, WebDriverManager uses the official Docker browser images created and maintained by the Selenium project, i.e., <a href="https://github.com/SeleniumHQ/docker-selenium">docker-selenium</a>. Therefore, the available browsers to be executed as Docker containers in WebDriverManager are Chrome (like the previous example), <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerFirefoxTest.java">Firefox</a>, <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerEdgeTest.java">Edge</a>, and <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerChromiumTest.java">Chromium</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Some <em>dockerized</em> browsers (e.g., Chromium and Edge) are supported in ARM64 architecture. See the <a href="https://github.com/SeleniumHQ/docker-selenium">docker-selenium</a> README for further information.
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="browser-versions"><a class="anchor" href="#browser-versions"></a>3.4.1. Browser Versions</h4>
<div class="paragraph">
<p>A significant aspect of the browser containers presented so far is that WebDriverManager connects to Docker Hub to discover the latest available release when the browser version is not specified (like the examples explained before). This way, the <em>dockerized</em> browsers of tests handled by WebDriverManager are auto-maintained, in the sense that these tests use the latest version available without any additional effort.</p>
</div>
<div class="paragraph">
<p>Nevertheless, we can force a given browser version in Docker using the method <code>browserVersion()</code> of the WebDriverManager API. This method accepts a <code>String</code> parameter specifying the version. This version can be fixed (e.g., <code>132.0</code>), and it also accepts the following wildcards:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>"latest"</code> : To specify the latest version explicitly (default option).</p>
</li>
<li>
<p><code>"latest-N"</code> : Where <code>N</code> is an integer value to be subtracted from the current stable version. For example, if we specify <code>latest-1</code> (i.e., <em>latest version minus one</em>), the previous version to the stable release will be used (see an example <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerChromeVersionsTest.java">here</a>).</p>
</li>
<li>
<p><code>"beta"</code>: To use the beta version.</p>
</li>
<li>
<p><code>"dev"</code>: To use the development version.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The following example shows a test using Chrome beta in Docker (see a similar example using Firefox dev <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerFirefoxDevTest.java">here</a>).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager.isDockerAvailable</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assumptions.assumeThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DockerChromeBetaTest</span> {

    WebDriver driver;

    WebDriverManager wdm = WebDriverManager.chromedriver().browserInDocker()
            .browserVersion(<span class="string"><span class="delimiter">&quot;</span><span class="content">beta</span><span class="delimiter">&quot;</span></span>);

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        assumeThat(isDockerAvailable()).isTrue();
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        wdm.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="remote-desktop"><a class="anchor" href="#remote-desktop"></a>3.4.2. Remote Desktop</h4>
<div class="paragraph">
<p>A possible inconvenience of using browsers in Docker is that we cannot see what is happening inside the container by default. To improve this situation, WebDriverManager allows connecting to the remote desktop session simply by invoking the method <code>enableVnc()</code> of a dockerized browser. When using this option, two different technologies are used internally:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Virtual Network Computing (<a href="https://en.wikipedia.org/wiki/Virtual_Network_Computing">VNC</a>), a graphical desktop sharing system. In WebDriverManager, a VNC server is started in the browser container.</p>
</li>
<li>
<p><a href="https://novnc.com/">noVNC</a>, a open-source web-based VNC client. In WebDriverManager, a custom <a href="https://github.com/bonigarcia/novnc">noVNC Docker image</a> is used to connect through noVNC.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The following example shows a test that enables this feature. WebDriverManager writes the noVNC URL in the <code>INFO</code> trace logs. In addition, as shown in this example, this URL can be found by invoking the method <code>getDockerNoVncUrl()</code>. We can use this URL to inspect and interact with the browser during the test execution (as shown in the following picture).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">java.net.URL</span>;
<span class="keyword">import</span> <span class="include">java.time.Duration</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DockerChromeVncTest</span> {

    WebDriver driver;

    WebDriverManager wdm = WebDriverManager.chromedriver().browserInDocker()
            .enableVnc();

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        wdm.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() <span class="directive">throws</span> <span class="exception">Exception</span> {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// Verify URL for remote session</span>
        <span class="predefined-type">URL</span> noVncUrl = wdm.getDockerNoVncUrl();
        assertThat(noVncUrl).isNotNull();

        <span class="comment">// Pause for manual inspection</span>
        <span class="predefined-type">Thread</span>.sleep(<span class="predefined-type">Duration</span>.ofSeconds(<span class="integer">60</span>).toMillis());
    }

}</code></pre>
</div>
</div>
<div id="vnc" class="imageblock thumb">
<div class="content">
<img src="img/wdm-vnc.png" alt="wdm vnc">
</div>
<div class="title">Figure 2. Example of noVNC session using Chrome in Docker</div>
</div>
</div>
<div class="sect3">
<h4 id="video-recording"><a class="anchor" href="#video-recording"></a>3.4.3. Video Recording</h4>
<div class="paragraph">
<p>The following related feature is recording the desktop session of a dockerized browser. To enable it, we need to invoke the method <code>enableRecording()</code> in WebDriverManager. Internally, WebDriverManager starts another Docker container using <a href="https://ffmpeg.org/">FFmpeg</a> to record the browser session. At the end of the test, we can find the recording in MP4 (by default, with a filename composed of the browser name followed by the symbol <code>_</code> and the system timestamp, plus another <code>_</code> and the session id) located in the project root folder (you can change this behavior using <a href="#advanced-configuration">configuration capabilities</a>. The following test shows an example of this feature.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">java.nio.file.Path</span>;
<span class="keyword">import</span> <span class="include">java.time.Duration</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.By</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DockerChromeRecordingTest</span> {

    WebDriver driver;

    WebDriverManager wdm = WebDriverManager.chromedriver().browserInDocker()
            .enableRecording();

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        wdm.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() <span class="directive">throws</span> <span class="exception">Exception</span> {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// Pause to see the navigation in the recording</span>
        <span class="predefined-type">Thread</span>.sleep(<span class="predefined-type">Duration</span>.ofSeconds(<span class="integer">2</span>).toMillis());

        driver.findElement(By.partialLinkText(<span class="string"><span class="delimiter">&quot;</span><span class="content">form</span><span class="delimiter">&quot;</span></span>)).click();

        <span class="comment">// Pause to generate a longer recording</span>
        <span class="predefined-type">Thread</span>.sleep(<span class="predefined-type">Duration</span>.ofSeconds(<span class="integer">2</span>).toMillis());

        <span class="comment">// Verify recoding file</span>
        Path recordingPath = wdm.getDockerRecordingPath();
        assertThat(recordingPath).exists();

        <span class="comment">// Get recording as base64</span>
        <span class="predefined-type">String</span> recordingBase64 = wdm.getRecordingBase64();
        assertThat(recordingBase64).isNotEmpty();
    }

}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="advanced-settings-2"><a class="anchor" href="#advanced-settings-2"></a>3.4.4. Advanced Settings</h4>
<div class="paragraph">
<p>The dockerized browser provided by WebDriverManager can be configured in different ways. For example, the WebDriverManager API allows using volumes, customizing the language and timezone inside the browser container, using custom images, configuring remote Docker daemon, customizing shared memory and in-memory filesystem (<em>tmpfs</em>), changing the screen resolution and video frame rate, or customizing the recording output (folder and filename). See the <a href="#advanced-configuration">advanced configuration</a> section for specific details about it.</p>
</div>
<div class="paragraph">
<p>In addition, the WebDriverManager API provides several methods to get the most of the dockerized browsers, namely:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>getDockerRecordingPath()</code>: Get path of the session recording.</p>
</li>
<li>
<p><code>getDockerNoVncUrl()</code>: Ger URL of the remote desktop noVNC session.</p>
</li>
<li>
<p><code>getDockerSeleniumServerUrl()</code>: Get the URL of the underlying Selenium Server (inside the container) that allows controlling the remote (dockerized) browser.</p>
</li>
<li>
<p><code>getDockerService()</code>: It allows access to the Docker service and client (based on <a href="https://github.com/docker-java/docker-java">docker-java</a>) to make custom operations with Docker containers (e.g., run commands in the browser container, see example <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerExecCommandInContainerTest.java">here</a>).</p>
</li>
<li>
<p><code>getDockerBrowserContainerId()</code>: Get browser container id (required for advance operation using the Docker client)</p>
</li>
<li>
<p><code>getWebDriver()</code>: Get the previously created <code>WebDriver</code> object (the same as the returned by the method <code>create()</code>.</p>
</li>
<li>
<p><code>getWebDriverList</code>: Get the previously created <code>WebDriver</code> objects (if any).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>A manager instance can be used to create more than one <code>WebDriver</code> object. For this reason, the methods <code>getDockerRecordingPath()</code>, <code>getDockerNoVncUrl()</code>, <code>getDockerSeleniumServerUrl()</code>, <code>getDockerBrowserContainerId()</code>, and <code>quit()</code> are overloaded, allowing to specify an <code>WebDriver</code> instance. When no parameter is specified in these methods, WebDriverManager returns the first <code>WebDriver</code> object (this is a usual case, i.e., a manager creates a single instance of <code>WebDriver</code>). You can see an example of a manager used to create more than one browser <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/docker/DockerSeveralBrowsersDifferentVersionsTest.java">here</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="browsers-monitoring"><a class="anchor" href="#browsers-monitoring"></a>3.5. Browsers Monitoring</h3>
<div class="paragraph">
<p>As of version 5.2.0, WebDriverManager provides seamless integration with <a href="https://bonigarcia.dev/browserwatcher/">BrowserWatcher</a>. BrowserWatcher is a browser extension designed to monitor different aspects of web browsers such as Chrome, Firefox, or Edge. This section summarizes the features of BrowserWatcher integrated into WebDriverManager.</p>
</div>
<div class="sect3">
<h4 id="console-log-gathering"><a class="anchor" href="#console-log-gathering"></a>3.5.1. Console Log Gathering</h4>
<div class="paragraph">
<p>Gathering the browser console might help find the cause of failed Selenium tests. BrowserWatcher provides a cross-browser mechanism based on JavaScript to implement this feature. WebDriverManager incorporates this feature for browsers controlled with Selenium WebDriver created through the WebDriverManager method <code>create()</code> and decorated with <code>watch()</code>. Internally,  BrowserWatcher is installed as a browser extension and starts gathering browser logs during the session time. At some point, we need to invoke the WebDriverManager method <code>getLogs()</code> to get the collected logs from the Java logic. The following test shows a basic example of this feature.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.invoke.MethodHandles.lookup</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.slf4j.LoggerFactory.getLogger</span>;

<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.Map</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.slf4j.Logger</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">GatherLogsFirefoxTest</span> {

    <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">Logger</span> log = getLogger(lookup().lookupClass());

    WebDriverManager wdm = WebDriverManager.firefoxdriver().watch();
    WebDriver driver;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setup() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(
                <span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/console-logs.html</span><span class="delimiter">&quot;</span></span>);

        <span class="predefined-type">List</span>&lt;<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt;&gt; logMessages = wdm.getLogs();

        assertThat(logMessages).hasSize(<span class="integer">5</span>);

        logMessages.forEach(map -&gt; log.debug(<span class="string"><span class="delimiter">&quot;</span><span class="content">[{}] [{}] {}</span><span class="delimiter">&quot;</span></span>,
                map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">datetime</span><span class="delimiter">&quot;</span></span>),
                <span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">&quot;</span><span class="content">%1$-14s</span><span class="delimiter">&quot;</span></span>,
                        map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">source</span><span class="delimiter">&quot;</span></span>).toString().toUpperCase() + <span class="string"><span class="delimiter">&quot;</span><span class="content">.</span><span class="delimiter">&quot;</span></span>
                                + map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">type</span><span class="delimiter">&quot;</span></span>).toString().toUpperCase()),
                map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">message</span><span class="delimiter">&quot;</span></span>)));
    }

}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
When using Firefox, this feature requires at least Selenium WebDriver 4.1.2.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="console-log-displaying"><a class="anchor" href="#console-log-displaying"></a>3.5.2. Console Log Displaying</h4>
<div class="paragraph">
<p>In addition to log gathering, BrowserWatcher allows displaying the console logs as dialog notifications on the page. This feature can be enabled using the method <code>watchAndDisplay()</code>, for example, as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.invoke.MethodHandles.lookup</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.slf4j.LoggerFactory.getLogger</span>;

<span class="keyword">import</span> <span class="include">java.time.Duration</span>;
<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.Map</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.slf4j.Logger</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DisplayLogsChromeTest</span> {

    <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">Logger</span> log = getLogger(lookup().lookupClass());

    WebDriverManager wdm = WebDriverManager.chromedriver().watchAndDisplay();
    WebDriver driver;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setup() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() <span class="directive">throws</span> <span class="exception">InterruptedException</span> {
        <span class="comment">// pause for manual browser inspection</span>
        <span class="predefined-type">Thread</span>.sleep(<span class="predefined-type">Duration</span>.ofSeconds(<span class="integer">3</span>).toMillis());

        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(
                <span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/console-logs.html</span><span class="delimiter">&quot;</span></span>);

        <span class="predefined-type">List</span>&lt;<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt;&gt; logMessages = wdm.getLogs();

        assertThat(logMessages).hasSize(<span class="integer">5</span>);

        logMessages.forEach(map -&gt; log.debug(<span class="string"><span class="delimiter">&quot;</span><span class="content">[{}] [{}] {}</span><span class="delimiter">&quot;</span></span>,
                map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">datetime</span><span class="delimiter">&quot;</span></span>),
                <span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">&quot;</span><span class="content">%1$-14s</span><span class="delimiter">&quot;</span></span>,
                        map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">source</span><span class="delimiter">&quot;</span></span>).toString().toUpperCase() + <span class="string"><span class="delimiter">&quot;</span><span class="content">.</span><span class="delimiter">&quot;</span></span>
                                + map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">type</span><span class="delimiter">&quot;</span></span>).toString().toUpperCase()),
                map.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">message</span><span class="delimiter">&quot;</span></span>)));
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following picture shows an example of these notifications:</p>
</div>
<div id="display-console-logs-example" class="imageblock thumb">
<div class="content">
<img src="img/display-console-logs-example-v3.png" alt="display console logs example v3">
</div>
<div class="title">Figure 3. Display logs example</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
This feature is not available for BrowserWatcher 2.x (i.e., based on manifest v3 -MV3-). Therefore, this feature is currently only available for Firefox.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="video-recording-2"><a class="anchor" href="#video-recording-2"></a>3.5.3. Video Recording</h4>
<div class="paragraph">
<p>BrowserWatcher (MV3) allows recording the current browser tab (i.e., the browser viewport). This feature requires a page loaded in the current tab (in other words, it cannot record empty or configuration pages). Then, the recording is started using the method <code>startRecording()</code> and stopped with <code>stopRecording()</code>. After stopping, the video recording will be automatically downloaded by the browser, and the recording (a webm file) will be stored in the downloads folder. You can discover the path of this recording using the method <code>getRecordingPath()</code>. Alternatively, you can get the recording in Base 64 using the method <code>getRecordingBase64()</code>. The following test illustrates this feature:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.invoke.MethodHandles.lookup</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.slf4j.LoggerFactory.getLogger</span>;

<span class="keyword">import</span> <span class="include">java.nio.file.Path</span>;
<span class="keyword">import</span> <span class="include">java.time.Duration</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Disabled</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.By</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.support.ui.ExpectedConditions</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.support.ui.WebDriverWait</span>;
<span class="keyword">import</span> <span class="include">org.slf4j.Logger</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="annotation">@Disabled</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">Broken in Chrome 142</span><span class="delimiter">&quot;</span></span>)
<span class="type">class</span> <span class="class">RecordChromeTest</span> {

    <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">Logger</span> log = getLogger(lookup().lookupClass());

    WebDriver driver;
    WebDriverManager wdm;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setup() {
        wdm = WebDriverManager.chromedriver().watch();
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(
                <span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/slow-calculator.html</span><span class="delimiter">&quot;</span></span>);

        wdm.startRecording();

        <span class="comment">// 1 + 3</span>
        driver.findElement(By.xpath(<span class="string"><span class="delimiter">&quot;</span><span class="content">//span[text()='1']</span><span class="delimiter">&quot;</span></span>)).click();
        driver.findElement(By.xpath(<span class="string"><span class="delimiter">&quot;</span><span class="content">//span[text()='+']</span><span class="delimiter">&quot;</span></span>)).click();
        driver.findElement(By.xpath(<span class="string"><span class="delimiter">&quot;</span><span class="content">//span[text()='3']</span><span class="delimiter">&quot;</span></span>)).click();
        driver.findElement(By.xpath(<span class="string"><span class="delimiter">&quot;</span><span class="content">//span[text()='=']</span><span class="delimiter">&quot;</span></span>)).click();

        <span class="comment">// ... should be 4, wait for it</span>
        WebDriverWait wait = <span class="keyword">new</span> WebDriverWait(driver, <span class="predefined-type">Duration</span>.ofSeconds(<span class="integer">10</span>));
        wait.until(ExpectedConditions.textToBe(By.className(<span class="string"><span class="delimiter">&quot;</span><span class="content">screen</span><span class="delimiter">&quot;</span></span>), <span class="string"><span class="delimiter">&quot;</span><span class="content">4</span><span class="delimiter">&quot;</span></span>));

        wdm.stopRecording();

        <span class="comment">// Get recording as path</span>
        Path recordingPath = wdm.getRecordingPath();
        assertThat(recordingPath).exists();
        log.debug(<span class="string"><span class="delimiter">&quot;</span><span class="content">Recording available at {}</span><span class="delimiter">&quot;</span></span>, recordingPath);

        <span class="comment">// Get recording as base64</span>
        <span class="predefined-type">String</span> recordingBase64 = wdm.getRecordingBase64();
        assertThat(recordingBase64).isNotEmpty();
    }

}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
This feature is based on the <a href="https://developer.chrome.com/docs/extensions/reference/tabCapture/">tabCapture API</a>. Therefore, it is only available in Chromium-based browsers (e.g., Chrome and Edge).
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="disabling-csp"><a class="anchor" href="#disabling-csp"></a>3.5.4. Disabling CSP</h4>
<div class="paragraph">
<p><a href="https://content-security-policy.com/">Content Security Policy (CSP)</a> is the name of an HTTP response header that browsers use to improve the security of web pages. CSP helps to protect from attacks such as cross-site scripting (XSS). Nevertheless, developers might want to disable the CSP headers received from the server for testing purposes. For this reason, BrowserWatcher allows bypassing these CSP headers. This feature (i.e., disabling CSP headers) can be enabled in WebDriverManager using the method <code>disableCsp()</code>, as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.invoke.MethodHandles.lookup</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.slf4j.LoggerFactory.getLogger</span>;

<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.Map</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.slf4j.Logger</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.wdm.WebDriverManager</span>;

<span class="type">class</span> <span class="class">DisableCspFirefoxTest</span> {

    <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">Logger</span> log = getLogger(lookup().lookupClass());

    WebDriverManager wdm = WebDriverManager.firefoxdriver().watch()
            .disableCsp();
    WebDriver driver;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setup() {
        driver = wdm.create();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        driver.quit();
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://paypal.com/</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">List</span>&lt;<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt;&gt; logMessages = wdm.getLogs();
        assertThat(logMessages).isNotNull();
    }

}</code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
This feature is currently only available for Firefox.
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="other-usages"><a class="anchor" href="#other-usages"></a>4. Other Usages</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In addition to as a regular Java dependency, WebDriverManager can be used in other use cases. This section summarizes these usages.</p>
</div>
<div class="sect2">
<h3 id="webdrivermanager-cli"><a class="anchor" href="#webdrivermanager-cli"></a>4.1. WebDriverManager CLI</h3>
<div class="paragraph">
<p>WebDriverManager can be used interactively from the Command Line Interface (CLI), i.e., the shell. There are three different ways to use WebDriverManager as a CLI tool:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Using the WebDriverManager <em>fat-JAR</em> (i.e., WebDriverManager with all its dependencies in a single executable JAR file). This JAR file is generated from the source using the Maven command <code>mvn compile assembly:single</code>, and it is released on GitHub with every new version of WebDriverManager. You can download the latest of this fat-JAR from <a href="https://github.com/bonigarcia/webdrivermanager/releases/download/webdrivermanager-6.3.3/webdrivermanager-6.3.3-fat.jar">here</a>. Once you get this file, you  need to use the following command in the shell (where <code>&lt;args&gt;</code> are the accepted arguments, explained below):</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">java -jar webdrivermanager-6.3.3-fat.jar &lt;args&gt;</code></pre>
</div>
</div>
</li>
<li>
<p>Using the source code. WebDriverManager is hosted on <a href="https://github.com/bonigarcia/webdrivermanager">GitHub</a>. We can use Maven to manage its Java source code. For example, to run the CLI mode using Maven and the source code, we need to invoke the following Maven command in the shell from the project root:</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">mvn exec:java -Dexec.args=&quot;&lt;args&gt;&quot;</code></pre>
</div>
</div>
</li>
<li>
<p>Using the WebDriverManager Docker container. Each new release of WebDriverManager is pushed to <a href="https://hub.docker.com/r/bonigarcia/webdrivermanager">Docker Hub</a> as a container based on <a href="https://hub.docker.com/_/openjdk">OpenJDK</a> plus the WebDriverManager fat-JAR. The default command to run the WebDriverManager Docker container is described below.</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">docker run --rm -e ARGS=&quot;&lt;args&gt;&quot; bonigarcia/webdrivermanager:6.3.3</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>WebDriverManager CLI can be used for three different purposes. We can see these options by launching the CLI with empty or invalid arguments (<code>&lt;args&gt;</code> in the commands before). In this case, the output of WebDriverManager CLI is the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">[ERROR] The valid arguments for WebDriverManager CLI are:
[ERROR] 1. For resolving drivers locally:
[ERROR]         resolveDriverFor browserName &lt;browserVersion&gt;
[ERROR] (where browserName is: chrome|edge|firefox|opera|chromium|iexplorer)
[ERROR]
[ERROR] 2. For running a browser in a Docker (and use it trough noVNC):
[ERROR]         runInDocker browserName &lt;browserVersion&gt;
[ERROR] (where browserName is: chrome|edge|firefox|chromium)
[ERROR]
[ERROR] 3. For starting WebDriverManager Server:
[ERROR]         server &lt;port&gt;
[ERROR] (where the default port is 4444)</code></pre>
</div>
</div>
<h4 id="option-1-driver-resolver" class="discrete">Option 1: Driver Resolver</h4>
<div class="paragraph">
<p>WebDriverManager CLI can be used to resolve drivers (e.g., chromedriver, geckodriver) by applying the usual <a href="#resolution-algorithm">resolution algorithm</a> from the shell. This feature can be interesting if we want to download drivers outside a Java program. To use this option, we need to invoke WebDriverManager CLI using the following arguments (supposing we need to resolve chromedriver):</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Fat-JAR:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">java -jar webdrivermanager-6.3.3-fat.jar resolveDriverFor chrome</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Source code:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">mvn exec:java -Dexec.args=&quot;resolveDriverFor chrome&quot;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Docker container:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">docker run --rm -v ${PWD}:/wdm -e ARGS=&quot;resolveDriverFor chrome&quot; bonigarcia/webdrivermanager:6.3.3</code></pre>
</div>
</div>
<h4 id="option-2-browsers-in-docker" class="discrete">Option 2: Browsers in Docker</h4>
<div class="paragraph">
<p>WebDriverManager CLI can execute browsers in Docker containers and interact with them using noVNC. This feature can be interesting for <em>exploratory testing</em> for web applications using different types and versions of web browsers. To use this option, the arguments we need to use are the following (supposing we want to use Chrome):</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Fat-JAR:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">java -jar webdrivermanager-6.3.3-fat.jar runInDocker chrome</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Source code:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">mvn exec:java -Dexec.args=&quot;runInDocker chrome&quot;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Docker container:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -e ARGS=&quot;runInDocker chrome&quot; bonigarcia/webdrivermanager:6.3.3</code></pre>
</div>
</div>
<h4 id="option-3-server" class="discrete">Option 3: Server</h4>
<div class="paragraph">
<p>Finally, WebDriverManager CLI allows starting the WebDriverManager Server, as follows (see <a href="#webdrivermanager-server">next section</a> for more details about it):</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Fat-JAR:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">java -jar webdrivermanager-6.3.3-fat.jar server</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Source code:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">mvn exec:java -Dexec.args=&quot;server&quot;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Docker container:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">docker run --rm -p 4444:4444 -v /var/run/docker.sock:/var/run/docker.sock bonigarcia/webdrivermanager:6.3.3</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="webdrivermanager-server"><a class="anchor" href="#webdrivermanager-server"></a>4.2. WebDriverManager Server</h3>
<div class="paragraph">
<p>The WebDriverManager Server is based on HTTP and offers two types of services. First, it can resolve drivers (chromedriver, geckodriver, etc.). The WebDriverManager Server exposes a simple REST-like API to this aim. WebDriverManager Server sends the resolved driver as an HTTP attachment in the response. The endpoints provided by this API are the following (supposing that WebDriverManager is running the localhost in its default port, i.e., 4444):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://localhost:4444/chromedriver">http://localhost:4444/chromedriver</a>: To resolve chromedriver.</p>
</li>
<li>
<p><a href="http://localhost:4444/firefoxdriver">http://localhost:4444/firefoxdriver</a>: To resolve geckodriver.</p>
</li>
<li>
<p><a href="http://localhost:4444/edgedriver">http://localhost:4444/edgedriver</a>: To resolve msedgedriver.</p>
</li>
<li>
<p><a href="http://localhost:4444/operadriver">http://localhost:4444/operadriver</a>: To resolve geckodriver.</p>
</li>
<li>
<p><a href="http://localhost:4444/iedriver">http://localhost:4444/iedriver</a>: To resolve geckodriver.</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You can parametrize these URLs using all the configuration keys available in WebDriverManager (see the <a href="#advanced-configuration">advanced configuration</a> section) as parameters, but removing the <code>wdm.</code> preffix. For instance, the URL for requesting the resolution of chromedriver for Chrome 100 would be <a href="http://localhost:4444/chromedriver?chromeVersion=100">http://localhost:4444/chromedriver?chromeVersion=100</a>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Second, the WebDriverManager Server acts as a regular Selenium Server (i.e., a <em>hub</em> in the classical Selenium Grid architecture). This feature can create remote <code>WebDriver</code> instances using the WebDriverManager Server (even for different language bindings than Java). The following example shows a Node.js test using Selenium WebDriver and WebDriverManager Server (notice that by default, the WebDriverManager Server URL does not require any path, i.e., <a href="http://localhost:4444/">http://localhost:4444/</a>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="javascript"><span class="keyword">var</span> webdriver = require(<span class="string"><span class="delimiter">&quot;</span><span class="content">selenium-webdriver</span><span class="delimiter">&quot;</span></span>);

async <span class="keyword">function</span> <span class="function">wdmServerTest</span>() {
    <span class="keyword">var</span> wdmServerUrl = <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:4444/</span><span class="delimiter">&quot;</span></span>;
    <span class="keyword">var</span> capabilities = {
        <span class="key">browserName</span> : <span class="string"><span class="delimiter">&quot;</span><span class="content">chrome</span><span class="delimiter">&quot;</span></span>,
        <span class="key">version</span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">100</span><span class="delimiter">&quot;</span></span>
    };

    <span class="keyword">try</span> {
        <span class="keyword">var</span> driver = await <span class="keyword">new</span> webdriver.Builder().usingServer(wdmServerUrl)
                .withCapabilities(capabilities).build();

        <span class="keyword">var</span> sutUrl = <span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>;
        await driver.get(sutUrl);

        await driver.getTitle().then(<span class="keyword">function</span>(title) {
            console.log(<span class="string"><span class="delimiter">&quot;</span><span class="content">The title of </span><span class="delimiter">&quot;</span></span> + sutUrl + <span class="string"><span class="delimiter">&quot;</span><span class="content"> is '</span><span class="delimiter">&quot;</span></span> + title + <span class="string"><span class="delimiter">&quot;</span><span class="content">'</span><span class="delimiter">&quot;</span></span>)
        });

    } <span class="keyword">catch</span> (err) {
        console.error(<span class="string"><span class="delimiter">&quot;</span><span class="content">Something went wrong!</span><span class="char">\n</span><span class="delimiter">&quot;</span></span>, err.stack);

    } <span class="keyword">finally</span> {
        <span class="keyword">if</span> (driver) {
            driver.quit();
        }
    }
}

wdmServerTest();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="webdrivermanager-agent"><a class="anchor" href="#webdrivermanager-agent"></a>4.3. WebDriverManager Agent</h3>
<div class="paragraph">
<p>WebDriverManager can also be used as <em>Java Agent</em>. In this case, and using the JVM instrumentation API, WebDriverManager intercepts calls to applications running on the JVM and modifies their bytecode. In particular, the WebDriverManager Agent uses this technique to check the objects being created in the JVM. Before Selenium WebDriver objects are instantiated (<code>ChromeDriver</code>, <code>FirefoxDriver</code>, etc.), the required manager is used to resolve its driver (chromedriver, geckodriver, etc.). Thanks to this approach, we can get rid of the WebDriverManager call (e.g. <code>WebDriverManager.chromedriver.setup();</code>) from our test, for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">org.junit.jupiter.api.AfterEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.BeforeEach</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.WebDriver</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.chrome.ChromeDriver</span>;

<span class="type">class</span> <span class="class">ChromeAgentTest</span> {

    WebDriver driver;

    <span class="annotation">@BeforeEach</span>
    <span class="type">void</span> setupTest() {
        driver = <span class="keyword">new</span> ChromeDriver();
    }

    <span class="annotation">@AfterEach</span>
    <span class="type">void</span> teardown() {
        <span class="keyword">if</span> (driver != <span class="predefined-constant">null</span>) {
            driver.quit();
        }
    }

    <span class="annotation">@Test</span>
    <span class="type">void</span> test() {
        <span class="comment">// Test logic</span>
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To configure the WebDriverManager Agent, we need to specify the path of the WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/releases/download/webdrivermanager-6.3.3/webdrivermanager-6.3.3-fat.jar">fat-JAR</a> using the JVM flag <code>-javaagent:/path/to/webdrivermanager-6.3.3-fat.jar</code>. Alternatively, it can be done using Maven (see a complete project example <a href="https://github.com/bonigarcia/wdm-agent-example/">here</a>).</p>
</div>
</div>
<div class="sect2">
<h3 id="selenium-jupiter"><a class="anchor" href="#selenium-jupiter"></a>4.4. Selenium-Jupiter</h3>
<div class="paragraph">
<p>WebDriverManager is the heart of the project <a href="https://github.com/bonigarcia/selenium-jupiter">Selenium-Jupiter</a>, an open-source JUnit 5 extension for Selenium WebDriver. Selenium-Jupiter uses the programming and extension model provided by JUnit 5 (named <a href="https://junit.org/junit5/docs/current/user-guide/">Jupiter</a>) together with WebDriverManager to create tests with reduced boilerplate code. For instance, thanks to the Jupiter feature for parameter resolution, we can declare a type of the <code>WebDriver</code> hierarchy (e.g., <code>ChromeDriver</code>) as a test parameter. Internally, Selenium-Jupiter resolves its driver and creates the instance before tests, and then the browser is gracefully closed at the end of the test. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">org.assertj.core.api.Assertions.assertThat</span>;

<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.Test</span>;
<span class="keyword">import</span> <span class="include">org.junit.jupiter.api.extension.ExtendWith</span>;
<span class="keyword">import</span> <span class="include">org.openqa.selenium.chrome.ChromeDriver</span>;

<span class="keyword">import</span> <span class="include">io.github.bonigarcia.seljup.SeleniumJupiter</span>;

<span class="annotation">@ExtendWith</span>(SeleniumJupiter.class)
<span class="type">class</span> <span class="class">ChromeTest</span> {

    <span class="annotation">@Test</span>
    <span class="type">void</span> test(ChromeDriver driver) {
        driver.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">https://bonigarcia.dev/selenium-webdriver-java/</span><span class="delimiter">&quot;</span></span>);
        assertThat(driver.getTitle()).contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">Selenium WebDriver</span><span class="delimiter">&quot;</span></span>);
    }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Selenium-Jupiter provides more different features, such as seamless integration with Docker, test templates (for cross-browser testing), conditional test execution (depending on the availability of browsers), conditional screenshots and recordings (when tests fail), and more. For further details, read the <a href="https://bonigarcia.dev/selenium-jupiter/">Selenium-Jupiter documentation</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="selenium-grid"><a class="anchor" href="#selenium-grid"></a>4.5. Selenium Grid</h3>
<div class="paragraph">
<p><a href="https://www.selenium.dev/documentation/en/grid/">Selenium Grid</a> is an infrastructure that allows serving remote browsers to be used in Selenium WebDriver tests. The nodes in which the browsers are executed in Selenium Grid also require managing the required drivers (chromedriver, geckodriver, etc.). To manage these drivers, as usual, we can use WebDriverManager.</p>
</div>
<div class="paragraph">
<p>For example, this <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/create/ChromeRemoteTest.java">test</a> shows how to start a Selenium Grid in standalone mode, resolving the driver with WebDriverManager and registering a Chrome browser in the Selenium Server. In addition, if we start Selenium Grid from the shell, we can complement it with WebDriverManager CLI to resolve the required drivers, as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">boni@ubuntu:~$ java -jar webdrivermanager-6.3.3-fat.jar resolveDriverFor chrome
[INFO] Using WebDriverManager to resolve chrome
[DEBUG] Detecting chrome version using online commands.properties
[DEBUG] Running command on the shell: [google-chrome, --version]
[DEBUG] Result: Google Chrome 103.0.5060.134
[DEBUG] Latest version of chromedriver according to https://chromedriver.storage.googleapis.com/LATEST_RELEASE_103 is 103.0.5060.134
[INFO] Using chromedriver 103.0.5060.134 (resolved driver for Chrome 103)
[INFO] Reading https://chromedriver.storage.googleapis.com/ to seek chromedriver
[DEBUG] Driver to be downloaded chromedriver 103.0.5060.134
[INFO] Downloading https://chromedriver.storage.googleapis.com/103.0.5060.134/chromedriver_linux64.zip
[INFO] Extracting driver from compressed file chromedriver_linux64.zip
[INFO] Driver location: /home/boni/chromedriver

boni@ubuntu:~$ java -jar selenium-server-4.3.0.jar standalone
01:31:52.806 INFO [LoggingOptions.configureLogEncoding] - Using the system default encoding
01:31:52.810 INFO [OpenTelemetryTracer.createTracer] - Using OpenTelemetry for tracing
01:31:53.344 INFO [NodeOptions.getSessionFactories] - Detected 16 available processors
01:31:53.359 INFO [NodeOptions.discoverDrivers] - Discovered 1 driver(s)
01:31:53.382 INFO [NodeOptions.report] - Adding Chrome for {&quot;browserName&quot;: &quot;chrome&quot;} 16 times
01:31:53.417 INFO [Node.&lt;init&gt;] - Binding additional locator mechanisms: id, relative, name
01:31:53.436 INFO [GridModel.setAvailability] - Switching Node 3804a261-5889-44c5-8cb1-d56162cf39ef (uri: http://172.18.0.1:4444) from DOWN to UP
01:31:53.436 INFO [LocalDistributor.add] - Added node 3804a261-5889-44c5-8cb1-d56162cf39ef at http://172.18.0.1:4444. Health check every 120s
01:31:53.554 INFO [Standalone.execute] - Started Selenium Standalone 4.3.0 (revision a4995e2c09*): http://172.18.0.1:4444</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="appium"><a class="anchor" href="#appium"></a>4.6. Appium</h3>
<div class="paragraph">
<p><a href="https://appium.io/">Appium</a> is a test automation framework for mobile applications. In the same way that Selenium WebDriver, Appium also speaks the W3C WebDriver protocol to drive web browsers on mobile devices. For this reason, the required driver must be present to control mobile browsers with Appium.</p>
</div>
<div class="paragraph">
<p>Again, WebDriverManager can help in this task. The following snippet shows a Java method that creates a <code>WebDriver</code> object using the driver path for the Chrome browser on an Android device:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">    <span class="directive">public</span> WebDriver createChromeAndroidDriver(<span class="predefined-type">String</span> browserVersion,
            <span class="predefined-type">String</span> deviceName, <span class="predefined-type">URL</span> appiumServerUrl) {
        <span class="comment">// Resolve driver and get its path</span>
        WebDriverManager wdm = WebDriverManager.chromedriver()
                .browserVersion(browserVersion);
        wdm.setup();
        <span class="predefined-type">String</span> chromedriverPath = wdm.getDownloadedDriverPath();

        <span class="comment">// Create WebDriver instance using the driver path</span>
        DesiredCapabilities capabilities = <span class="keyword">new</span> DesiredCapabilities();
        capabilities.setCapability(<span class="string"><span class="delimiter">&quot;</span><span class="content">browserName</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">chrome</span><span class="delimiter">&quot;</span></span>);
        capabilities.setCapability(<span class="string"><span class="delimiter">&quot;</span><span class="content">version</span><span class="delimiter">&quot;</span></span>, browserVersion);
        capabilities.setCapability(<span class="string"><span class="delimiter">&quot;</span><span class="content">deviceName</span><span class="delimiter">&quot;</span></span>, deviceName);
        capabilities.setCapability(<span class="string"><span class="delimiter">&quot;</span><span class="content">platformName</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">android</span><span class="delimiter">&quot;</span></span>);
        capabilities.setCapability(<span class="string"><span class="delimiter">&quot;</span><span class="content">chromedriverExecutable</span><span class="delimiter">&quot;</span></span>, chromedriverPath);

        <span class="keyword">return</span> <span class="keyword">new</span> AndroidDriver&lt;WebElement&gt;(appiumServerUrl, capabilities);
    }</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="examples"><a class="anchor" href="#examples"></a>5. Examples</h2>
<div class="sectionbody">
<div class="paragraph">
<p>All the examples presented in this documentation are available in the <a href="https://github.com/bonigarcia/webdrivermanager/tree/master/src/test/java/io/github/bonigarcia/wdm/test/">WebDriverManager tests</a>. Moreover, different public repositories contain test examples using WebDriverManager, such as:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/bonigarcia/webdrivermanager-basic">WebDriverManager Basic</a>: A simple project using JUnit 5, Selenium WebDriver, and WebDriverManager.</p>
</li>
<li>
<p><a href="https://github.com/bonigarcia/webdrivermanager-examples">WebDriverManager Examples</a>: Different examples with JUnit 5, Selenium WebDriver, and WebDriverManager.</p>
</li>
<li>
<p><a href="https://github.com/bonigarcia/webdrivermanager-spring-boot">WebDriverManager Spring-Boot</a>: Another simple project using Spring-Boot, JUnit 5, Selenium WebDriver, and WebDriverManager.</p>
</li>
<li>
<p><a href="https://github.com/bonigarcia/wdm-agent-example">WebDriverManager Agent Example</a>: Maven project using WebDriverManager as Agent.</p>
</li>
<li>
<p><a href="https://github.com/bonigarcia/selenium-webdriver-java">Selenium WebDriver with Java</a>: A comprehensive collection of Selenium WebDriver 4 examples using Java as language binding.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="advanced-configuration"><a class="anchor" href="#advanced-configuration"></a>6. Advanced Configuration</h2>
<div class="sectionbody">
<div class="paragraph">
<p>WebDriverManager provides different ways of configuration. First, by using its <em>Java API</em>. To that aim, each manager (e.g., <code>chromedriver()</code>, <code>firefoxdriver()</code>, etc., allows for concatenating different methods of this API to specify custom options or preferences. For example (the explanation of these methods and the other possibilities are explained in the tables at the end of this section):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">WebDriverManager.chromedriver().driverVersion(<span class="string"><span class="delimiter">&quot;</span><span class="content">81.0.4044.138</span><span class="delimiter">&quot;</span></span>).setup();
WebDriverManager.firefoxdriver().browserVersion(<span class="string"><span class="delimiter">&quot;</span><span class="content">75</span><span class="delimiter">&quot;</span></span>).setup();
WebDriverManager.operadriver().proxy(<span class="string"><span class="delimiter">&quot;</span><span class="content">server:port</span><span class="delimiter">&quot;</span></span>).setup();
WebDriverManager.edgedriver().mac().setup();</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
In addition to the methods presented in this section, each manager provides a <code>config()</code> method that configures all the possible parameters of WebDriverManager. In addition, WebDriverManager provides the metho <code>reset()</code> to restore to the default values the parameters of a given manager.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The second alternative to tune WebDriverManager is using <em>Java system properties</em>. In this method, each WebDriverManager API method has its equivalence of a unique <em>configuration key</em>. For instance, the API method <code>cachePath()</code> (used to specify the driver cache folder) is equivalent to the configuration key <code>wdm.cachePath</code>. These types of configuration keys can be passed when executing the tests, for example, using Maven:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">mvn test -Dwdm.cachePath=/custom/path/to/driver/cache</code></pre>
</div>
</div>
<div class="paragraph">
<p>The third way to configure WebDriverManager is using <em>environmental variables</em>. The names for these variables are made by converting each configuration key name (e.g., <code>wdm.cachePath</code>) to uppercase and replacing the symbol <code>.</code> with <code>_</code> (e.g., <code>WDM_CACHEPATH</code>). These variables can be helpful to global setup parameters at the operating system level. Also, it allows specifying a custom setup when using WebDriverManager as a Docker container. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">docker run --rm -v ${PWD}:/wdm -e ARGS=&quot;resolveDriverFor chrome&quot; -e WDM_CHROMEVERSION=84 bonigarcia/webdrivermanager:6.3.3</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The preference order of these configuration alternatives is (in case of overlapping) is: 1) Environmental Variables. 2) Java system properties. 3) Java API.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The remainder of this section describes all the possible Java methods in the WebDriverManager API and its equivalent configuration keys in three groups of capabilities: <a href="#driver_management_config">driver management</a>, <a href="#docker_config">browsers in Docker</a>, and <a href="#server_config">WebDriverManager Server</a>.</p>
</div>
<table id="driver_management_config" class="tableblock frame-all grid-all stretch">
<caption class="title">Table 1. Configuration capabilities for driver management</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">API method</th>
<th class="tableblock halign-left valign-top">Configuration key</th>
<th class="tableblock halign-left valign-top">Default value</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>cachePath(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.cachePath</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>~/.cache/selenium</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Folder to store drivers locally</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>resolutionCachePath(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.resolutionCachePath</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>~/.cache/selenium</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Folder to store the resolution cache</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>driverVersion(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.chromeDriverVersion</code>, <code>wdm.operaDriverVersion</code>, <code>wdm.iExplorerDriverVersion</code>, <code>wdm.edgeDriverVersion</code>, <code>wdm.geckoDriverVersion</code>, <code>wdm.chromiumDriverVersion</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic driver version discovery through the <a href="#resolution-algorithm">resolution algorithm</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom driver version</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>browserVersion(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.chromeVersion</code>, <code>wdm.operaVersion</code>, <code>wdm.edgeVersion</code>, <code>wdm.firefoxVersion</code>, <code>wdm.chromiumVersion</code>, <code>wdm.safariVersion</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic browser version detection using the <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom browser version (major)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>browserBinary(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.chromeBinary</code>, <code>wdm.operaBinary</code>, <code>wdm.edgeBinary</code>, <code>wdm.firefoxBinary</code>, <code>wdm.chromiumBinary</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic browser version detection using the default browser locations as specified in the <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom browser binary path</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>forceDownload()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.forceDownload=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (drivers in cache are reused if available)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force downloading driver (even if it is already in the cache)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>useBetaVersions()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.useBetaVersions=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (driver versions are skipped)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Allow the use beta versions (if possible)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>architecture(Architecture)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.architecture</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic architecture discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force a given architecture for a driver</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arch32()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.architecture=X32</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic architecture discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Use 32-bit driver version</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arch64()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.architecture=X64</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic architecture discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Use 64-bit driver version</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arm64()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.architecture=ARM64</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic architecture discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Use ARM (Aarch) 64-bit driver version</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>operatingSystem(Operating</code> <code>System)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.os</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic OS discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force a given operating system (<code>WIN</code>, <code>LINUX</code>,  or <code>MAC</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>win()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.os=WIN</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic OS discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force Windows</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>linux()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.os=LINUX</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic OS discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force Linux</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mac()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.os=MAC</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic OS discovery)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force Mac OS</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>driverRepositoryUrl(URL)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.chromeDriverUrl</code>, <code>wdm.operaDriverUrl</code>, <code>wdm.edgeDriverUrl</code>, <code>wdm.geckoDriverUrl</code>, <code>wdm.iExplorerDriverUrl</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Driver specific URLs (available in <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/webdrivermanager.properties">webdrivermanager.properties</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Change the repository URL in which the drivers are hosted</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>useMirror()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.useMirror=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (not using mirrors)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enable the use of a driver repository <a href="https://registry.npmmirror.com/binary.html">mirror</a> (available for chromedriver, geckodriver, and operadriver)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>proxy(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.proxy</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (no proxy)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Use an HTTP proxy
for the network connection (using the notation <code>proxy:port</code> or
<code>username:password@proxy:port</code>). It can be also configured using the environment variable <code>HTTPS_PROXY</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>proxyUser(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.proxyUser</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (no proxy user)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Username for the
HTTP proxy. It can be also configured using the environment variable  <code>HTTPS_PROXY_USER</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>proxyPass(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.proxyPass</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (no proxy password)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Password for HTTP proxy. It can be also configured using the environment variable <code>HTTPS_PROXY_PASS</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>gitHubToken(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.gitHubToken</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (no GitHub token)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Personal access token for authenticated GitHub requests (see <a href="#known-issues">known issues</a>). It can be also configured using the environment variable <code>GITHUB_TOKEN</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ignoreVersions(String&#8230;&#8203;)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.ignoreVersions</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (no ignored versions)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Ignore specific driver version(s)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>timeout(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.timeout</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>30</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Timeout (in seconds) to connect and download drivers from online repositories</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>properties(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.properties</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>webdrivermanager.properties</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Properties file (in the project classpath) for default configuration values</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidExport()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidExport=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (export driver paths as Java properties (e.g. <code>webdriver.chrome.driver</code>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid step 4 in the <a href="#resolution-algorithm">resolution algorithm</a> (for instance, in the <a href="#webdrivermanager-cli">CLI</a> mode)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>exportParameter(String)</code> <code>exportParameter(Driver</code> <code>ManagerType)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.chromeDriverExport</code>, <code>wdm.geckoDriverExport</code>, <code>wdm.edgeDriverExport</code>, <code>wdm.iExplorerDriverExport</code>, <code>wdm.operaDriverExport</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Java property name used to export the driver path (available in <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/webdrivermanager.properties">webdrivermanager.properties</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Set custom property name. An <code>DriverManagerType</code> enum (e.g., <code>CHROME</code>, <code>FIREFOX</code>, etc.) can be used insted of the <code>String</code> parameter</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidOutputTree()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidOutputTree=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (create output tree in the driver cache, e.g., <code>chromedriver/linux64/2.37</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid output tree (for instance, in the <a href="#webdrivermanager-cli">CLI</a> mode)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidFallback()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidFallback=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (use a retries mechanism if any problem happens during the resolution algorithm)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid the fallback mechanism</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidBrowserDetection()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidBrowserDetection</code> <code>=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (browser version is detected, and the corresponding driver version s discovered)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Force to use the latest version available for a given driver</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidTmpFolder()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidTmpFolder=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (Each driver release (typically compressed) is copied in a temporal folder in the local machine, and then the driver is extracted and copied to the driver cache</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid using a temporal folder to download drivers (and handle driver release directly on driver cache)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidShutdownHook()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidShutdownHook=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid shutdown hook for drivers objected created with <code>create()</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>avoidExternalConnections()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.avoidExternalConnections=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid connections to external urls, useful when downloading webdrivers from an artifact storage in an intranet</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>browserVersionDetection</code> <code>Command(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.browserVersion</code> <code>DetectionCommand</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code> (automatic discovery using the <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom browser version detection command (see example <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/test/java/io/github/bonigarcia/wdm/test/chrome/ChromeBetaTest.java">here</a>)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>useLocalCommandsProperties</code> <code>First()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.commandsProperties</code> <code>OnlineFirst=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (the online <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/versions.properties">commands database</a> is used in the <a href="#resolution-algorithm">resolution algorithm</a>)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Use local copy of the commands database</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>commandsPropertiesUrl(URL)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.commandsPropertiesUrl</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Raw version of the online <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/src/main/resources/commands.properties">commands database</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Change commands database URL</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>clearDriverCache()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.clearDriverCache=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (not cleaning driver cache)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Clean driver cache</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>clearResolutionCache()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.clearResolutionCache</code> <code>=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code> (not cleaning resolution cache)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Clean resolution cache</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ttl(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.ttl</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>86400</code> (i.e., 1 day)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">TTL in seconds in which the resolved driver versions are valid in the resolution cache.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ttlBrowsers(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.ttlForBrowsers</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>3600</code> (i.e., 1 hour)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">TTL value in seconds in which the browser versions are valid in the resolution cache (also used for dockerized browsers).</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>disableTracing()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.tracing=false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code> (<code>RemoteWebDriver</code> tracing enabled by default)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Disable OpenTelemetry tracing for <code>RemoteWebDriver</code></p></td>
</tr>
</tbody>
</table>
<table id="docker_config" class="tableblock frame-all grid-all stretch">
<caption class="title">Table 2. Configuration capabilities for browsers in Docker</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">API method</th>
<th class="tableblock halign-left valign-top">Configuration key</th>
<th class="tableblock halign-left valign-top">Default value</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerDaemonUrl(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerDaemonUrl</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">URL of remote <a href="https://docs.docker.com/engine/reference/commandline/dockerd/">Docker daemon</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerTimezone(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerTimezone</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Etc/UTC</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Timezone</a> of the browser container</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerNetwork(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerNetwork</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>bridge</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Docker network name</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerLang(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerLang</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>EN</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">Language</a> of the browser container</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerShmSize(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerShmSize</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>512m</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Docker <a href="https://docs.docker.com/engine/reference/run/">shared memory</a> in bytes. Unit is optional and can be <code>b</code> (bytes), <code>k</code> (kilobytes), <code>m</code> (megabytes), or <code>g</code> (gigabytes)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerTmpfsSize(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerTmpfsSize</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>128m</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Docker in-memory filesystem (<a href="https://docs.docker.com/engine/reference/run/#tmpfs-mount-tmpfs-filesystems">tmpfs</a>). Units follows the same approach than the shared memory</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerTmpfsMount(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerTmpfsMount</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>/tmp</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Mount point for in-memory filesystem</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerStopTimeoutSec(Integer)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerStopTimeoutSec</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>5</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Max time to kill a container (in seconds) after stopping it</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>enableVnc()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerEnableVnc=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enable <a href="#remote-desktop">desktop remote session</a> for browsers in Docker</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>viewOnly()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerViewOnly=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Run remote desktop session (noVNC) in view-only mode</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>enableRecording()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerEnableRecording=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enable the <a href="#recordings">recordings</a> of the browser session in Docker</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerRecordingFrameRate(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerRecordingFrameRate</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>15</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Frame rate for recordings</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerScreenResolution(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerScreenResolution</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>1920x1080x24</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Screen resolution of the browser desktop session in format <code>&lt;width&gt;x&lt;height&gt;x&lt;colors-depth&gt;</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerRecordingOutput(String)</code> <code>dockerRecordingOutput(Path)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerRecordingOutput</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>.</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Path for the recording output. This value can be a folder or complete path (if it ends with <code>.mp4</code>)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerRecordingPrefix(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerRecordingPrefix</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Browser name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Prefix to be appended to default filname (i.e., broser name plus <code>_</code> plus session id)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerCustomImage(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerCustomImage</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom image to be used as browser in Docker</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerVolumes(String[])</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerVolumes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Docker volumes (single or array) using the format <code>"\local\path:\container\path"</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerExtraHosts(String[])</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerExtraHosts</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Docker Extra Hosts (single or array) using the format <code>"hostname:IP" ("host1:192.168.48.82,host2:192.168.48.16")</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerEnvVariables(String[])</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerEnvVariables</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Environment variable for Docker containers`</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerDefaultArgs(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerDefaultArgs</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>--disable-gpu</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Default arguments to start Docker containers`</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerPrivateEndpoint(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerPrivateEndpoint</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>""</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Used to prefix pull images when you have a private registry with authentication i.e docker-hub-remote.myprivate.com will be prefixed to pull as docker-hub-remote.myprivate.com/selenoid/vnc and so on for any images used (video recorder, novnc etc..), docker login docker-hub-remote.myprivate.com is still required in order to get the auth credentials stored in the .docker/config.json, for MacOS users make sure to configure your engine to store the credentials in the config.json instead of keychain storage.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>dockerAvoidPulling()</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.dockerAvoidPulling=true</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoid pulling Docker images from Docker Hub</p></td>
</tr>
</tbody>
</table>
<table id="server_config" class="tableblock frame-all grid-all stretch">
<caption class="title">Table 3. Configuration capabilities for WebDriverManager Server</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">API method</th>
<th class="tableblock halign-left valign-top">Configuration key</th>
<th class="tableblock halign-left valign-top">Default value</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>serverPort(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.serverPort</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>4444</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Port of WebDriverManager Server</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>serverPath(String)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.serverPath</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Path of WebDriverManager Server</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>serverTimeoutSec(int)</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>wdm.serverTimeoutSec</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>60</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Timeout (in seconds) for WebDriverManager server</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect1">
<h2 id="known-issues"><a class="anchor" href="#known-issues"></a>7. Known Issues</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="http-response-code-403"><a class="anchor" href="#http-response-code-403"></a>7.1. HTTP response code 403</h3>
<div class="paragraph">
<p>Some of the drivers (e.g., geckodriver or operadriver) are hosted on GitHub. When external clients (like WebDriverManager) make many consecutive requests to GitHub, and due to its traffic rate limit, it eventually responds with an HTTP 403 error (<em>forbidden</em>), as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>io.github.bonigarcia.wdm.config.WebDriverManagerException: Error HTTP 403 executing https://api.github.com/repos/mozilla/geckodriver/releases
    at io.github.bonigarcia.wdm.online.HttpClient.execute(HttpClient.java:172)
    at io.github.bonigarcia.wdm.WebDriverManager.openGitHubConnection(WebDriverManager.java:1266)
    at io.github.bonigarcia.wdm.WebDriverManager.getDriversFromGitHub(WebDriverManager.java:1280)
    at io.github.bonigarcia.wdm.managers.FirefoxDriverManager.getDriverUrls(FirefoxDriverManager.java:95)
    at io.github.bonigarcia.wdm.WebDriverManager.createUrlHandler(WebDriverManager.java:1111)
    at io.github.bonigarcia.wdm.WebDriverManager.download(WebDriverManager.java:959)
    at io.github.bonigarcia.wdm.WebDriverManager.manage(WebDriverManager.java:877)
    at io.github.bonigarcia.wdm.WebDriverManager.fallback(WebDriverManager.java:1106)
    at io.github.bonigarcia.wdm.WebDriverManager.handleException(WebDriverManager.java:1083)
    at io.github.bonigarcia.wdm.WebDriverManager.manage(WebDriverManager.java:883)
    at io.github.bonigarcia.wdm.WebDriverManager.setup(WebDriverManager.java:328)</code></pre>
</div>
</div>
<div class="paragraph">
<p>To avoid this problem, WebDriverManager can make authenticated requests using a <a href="https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token">personal access token</a>. See the <a href="#advanced-configuration">advanced configuration</a> section to discover how to set up this token in WebDriverManager.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
As of WebDriverManager 5.3.0, this issue should not happen anymore (even without a GitHub token). To avoid the 403 error, an automated job in GitHub Actions mirrors the GitHub API responses twice a day. This mirror is used internally by WebDriverManager instead of querying the GitHub API.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="testing-localhost-in-docker"><a class="anchor" href="#testing-localhost-in-docker"></a>7.2. Testing localhost in Docker</h3>
<div class="paragraph">
<p>A typical case in web development is testing a web application deployed in the localhost. In this case, and when using browsers in Docker containers, we need to know that the address <code>localhost</code> inside a Docker container is not the host&#8217;s address but the container address. To solve this problem, we can take different approaches. In Linux, we can use the gateway address for inter-container communication. This address is usually <code>172.17.0.1</code>, and can be discovered as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">$ ip addr show docker0
4: docker0: &lt;NO-CARRIER,BROADCAST,MULTICAST,UP&gt; mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:b4:83:10:c8 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever</code></pre>
</div>
</div>
<div class="paragraph">
<p>When the host is Mac OS or Windows, we can use the DNS name <code>host.docker.internal</code>, which will be resolved to the internal IP address used by the host.</p>
</div>
</div>
<div class="sect2">
<h3 id="chrome-92-94-in-docker"><a class="anchor" href="#chrome-92-94-in-docker"></a>7.3. Chrome 92-94 in Docker</h3>
<div class="paragraph">
<p>An <a href="https://bugs.chromium.org/p/chromedriver/issues/detail?id=3857">issue</a> affected Chrome 92 to 94 and Docker. The problem can be solved by using the argument <code>--disable-gpu</code> in Chrome and Edge. This argument is used by WebDriverManager 5 to avoid the issue. Nevertheless, some situations are still impossible to fix (e.g., when using <a href="https://hub.docker.com/r/bonigarcia/webdrivermanager">WebDriverManager in Docker</a> as CLI or Server) for Chrome 92 to 94 in Docker.</p>
</div>
</div>
<div class="sect2">
<h3 id="support-for-chromedriver-115"><a class="anchor" href="#support-for-chromedriver-115"></a>7.4. Support for chromedriver 115+</h3>
<div class="paragraph">
<p>The chromedriver team has stopped publishing the chromedriver releases and metadata using their traditional <a href="https://chromedriver.chromium.org/downloads">chromedriver download repository</a> with chromedriver 114. This way, as of <strong>chromedriver 115</strong>, the chromedriver releases can only be discovered programmatically using the  <a href="https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints">Chrome for Testing (CfT) JSON endpoints</a>.</p>
</div>
<div class="paragraph">
<p>This change is very relevant for WebDriverManager, since, as of Chrome 115 and 116, chromedriver cannot be managed automatically by WebDriverManager using the traditional way. Therefore, for older versions of WebDriverManager, this situation led to errors like the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>io.github.bonigarcia.wdm.online.HttpClient: Error HTTP 404 executing https://chromedriver.storage.googleapis.com/LATEST_RELEASE_116</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>org.openqa.selenium.SessionNotCreatedException: Could not start a new session. Response code 500. Message: session not created: This version of ChromeDriver only supports Chrome version 114</code></pre>
</div>
</div>
<div class="paragraph">
<p>WebDriverManager 5.4+ implements the support for the CfT endpoints. Therefore, the solution to this problem is to bump WebDriverManager to the <strong>latest version</strong> (6.3.3 currently). Also, to ensure that the wrong version has not been cached in the <strong>resolution cache</strong>, you can refresh completely the cache folder (at least once) as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">WebDriverManager.chromedriver().clearDriverCache().setup();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="connectivity-issues"><a class="anchor" href="#connectivity-issues"></a>7.5. Connectivity issues</h3>
<div class="paragraph">
<p>WebDriverManager must request remote endpoints (like the Chrome for Testing (CfT) endpoints) and download drivers from online repositories. When this operation is done in a corporate environment with a proxy or firewall, it might lead to connectivity problems like the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>Error HTTP 403 executing https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/win64/chromedriver-win64.zip</code></pre>
</div>
</div>
<div class="paragraph">
<p>If this is your case, consider the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Use the proxy capabilities of WebDriverManager to set up your proxy, e.g.:</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>WebDriverManager.chromedriver().proxy(&quot;myproxy:port&quot;).setup();</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Review your network setup to enable the remote requests and downloads required by WebDriverManager.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="troubleshooting"><a class="anchor" href="#troubleshooting"></a>8. Troubleshooting</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For getting the WebDriverManager logs, you need to include a Logback configuration file (for example, like <a href="https://github.com/bonigarcia/webdrivermanager-basic/blob/master/src/main/resources/logback.xml">this</a>) in your project classpath. Then, you need to set the level to <code>DEBUG</code>, or <code>TRACE</code> for finer details, e.g.:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>&lt;logger name=&quot;io.github.bonigarcia&quot; level=&quot;TRACE&quot; /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>After that, when running a test, your should see something like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>2023-08-25 13:10:11 [main] DEBUG  i.g.bonigarcia.wdm.WebDriverManager.&lt;init&gt;(227) - Using WebDriverManager {project-version}
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.versions.VersionDetector.getVersionsInputStream(364) - Reading online commands.properties to find out driver version
2023-08-25 13:10:12 [main] DEBUG i.g.b.wdm.versions.VersionDetector.getBrowserVersionFromTheShell(253) - Detecting chrome version using online commands.properties
2023-08-25 13:10:12 [main] DEBUG i.g.bonigarcia.wdm.versions.Shell.runAndWaitArray(65) - Running command on the shell: [cmd.exe, /C, wmic, datafile, where, name=&quot;%PROGRAMFILES:\=\\%\\Google\\Chrome\\Application\\chrome.exe&quot;, get, Version, /value]
2023-08-25 13:10:12 [main] DEBUG i.g.bonigarcia.wdm.versions.Shell.runAndWaitArray(69) - Result: Version=116.0.5845.110
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.versions.VersionDetector.getBrowserVersionUsingCommand(332) - Detected browser version is 116.0.5845.110
2023-08-25 13:10:12 [main] INFO  i.g.bonigarcia.wdm.WebDriverManager.resolveDriverVersion(1280) - Using chromedriver 116.0.5845.96 (resolved driver for Chrome 116)
2023-08-25 13:10:12 [main] DEBUG i.g.b.wdm.cache.ResolutionCache.putValueInResolutionCacheIfEmpty(119) - Storing resolution chrome=116 in cache (valid until 14:10:12 25/08/2023 CEST)
2023-08-25 13:10:12 [main] DEBUG i.g.b.wdm.cache.ResolutionCache.putValueInResolutionCacheIfEmpty(119) - Storing resolution chrome116=116.0.5845.96 in cache (valid until 13:10:12 26/08/2023 CEST)
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.getDriverFromCache(83) - Checking if chromedriver exists in cache
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.filterCacheBy(68) - Filter cache by chromedriver -- input list [C:\Users\boni\.cache\selenium\resolution.properties] -- output list []
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.filterCacheBy(68) - Filter cache by 116.0.5845.96 -- input list [] -- output list []
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.filterCacheBy(68) - Filter cache by WIN -- input list [] -- output list []
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.getDriverFromCache(105) - Avoid filtering for architecture 64 with chromedriver in Windows
2023-08-25 13:10:12 [main] TRACE i.g.b.wdm.cache.CacheHandler.getDriverFromCache(119) - chromedriver not found in cache
2023-08-25 13:10:12 [main] INFO  i.g.bonigarcia.wdm.WebDriverManager.logSeekRepo(1631) - Reading https://chromedriver.storage.googleapis.com/ to seek chromedriver
2023-08-25 13:10:12 [main] DEBUG i.g.bonigarcia.wdm.WebDriverManager.createUrlHandler(1516) - Driver to be downloaded chromedriver 116.0.5845.96
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.WebDriverManager.createUrlHandler(1525) - Driver URLs after filtering for version: []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByOs(141) - URLs before filtering by OS (WIN): []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByOs(145) - URLs after filtering by OS (WIN): []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByArch(151) - URLs before filtering by architecture (64): []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByArch(160) - URLs after filtering by architecture (64): []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByBeta(127) - URLs before filtering by beta versions: []
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.UrlHandler.filterByBeta(134) - URLs after filtering by beta versions: []
2023-08-25 13:10:12 [main] DEBUG i.g.bonigarcia.wdm.WebDriverManager.buildUrl(156) - Using URL built from repository pattern: https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/win64/chromedriver-win64.zip
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.getTarget(123) - Target file for URL https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/win64/chromedriver-win64.zip driver version 116.0.5845.96 = C:\Users\boni\.cache\selenium\chromedriver\win64\116.0.5845.96/chromedriver-win64.zip
2023-08-25 13:10:12 [main] INFO  i.g.bonigarcia.wdm.online.Downloader.downloadAndExtract(131) - Downloading https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/win64/chromedriver-win64.zip
2023-08-25 13:10:12 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.downloadAndExtract(137) - Target folder C:\Users\boni\.cache\selenium\chromedriver\win64\116.0.5845.96 ... using temporal file C:\Users\boni\AppData\Local\Temp\15824197595769158441\chromedriver-win64.zip
2023-08-25 13:10:13 [main] INFO  i.g.bonigarcia.wdm.online.Downloader.extract(192) - Extracting driver from compressed file chromedriver-win64.zip
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.unZip(220) - Unzipping chromedriver-win64/LICENSE.chromedriver (size: 215994 KB, compressed size: 35448 KB)
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.unZip(220) - Unzipping chromedriver-win64/chromedriver.exe (size: 14312960 KB, compressed size: 7094251 KB)
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.deleteFile(330) - Deleting file C:\Users\boni\AppData\Local\Temp\15824197595769158441\chromedriver-win64.zip
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.WebDriverManager.postDownload(1371) - Found driver in post-download: C:\Users\boni\AppData\Local\Temp\15824197595769158441\chromedriver-win64\chromedriver.exe
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.deleteFile(330) - Deleting file C:\Users\boni\AppData\Local\Temp\15824197595769158441\chromedriver-win64\LICENSE.chromedriver
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.deleteFolder(340) - Deleting folder C:\Users\boni\AppData\Local\Temp\15824197595769158441
2023-08-25 13:10:13 [main] TRACE i.g.bonigarcia.wdm.online.Downloader.downloadAndExtract(163) - Driver after extraction C:\Users\boni\.cache\selenium\chromedriver\win64\116.0.5845.96\chromedriver.exe
2023-08-25 13:10:13 [main] INFO  i.g.bonigarcia.wdm.WebDriverManager.exportDriver(1333) - Exporting webdriver.chrome.driver as C:\Users\boni\.cache\selenium\chromedriver\win64\116.0.5845.96\chromedriver.exe</code></pre>
</div>
</div>
<div class="paragraph">
<p>For further info about logging with SLF4J and Logback you can see the following <a href="https://www.stubbornjava.com/posts/logging-in-java-with-slf4j-and-logback">tutorial</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="community"><a class="anchor" href="#community"></a>9. Community</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are two ways to try to get community support related to WebDriverManager. First, questions can be discussed in <a href="https://stackoverflow.com/questions/tagged/webdrivermanager_java">StackOverflow</a>, using the tag <em>webdrivermanager_java</em>. In addition, comments, suggestions, and bug-reporting should be made using the <a href="https://github.com/bonigarcia/webdrivermanager/issues">GitHub issues</a>. Finally, if you think WebDriverManager can be enhanced, consider contributing to the project through a  <a href="https://github.com/bonigarcia/webdrivermanager/pulls">pull request</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="support"><a class="anchor" href="#support"></a>10. Support</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://opencollective.com/webdrivermanager">WebDriverManager</a> is part of <a href="https://opencollective.com/bonigarcia">OpenCollective</a>, an online funding platform for open and transparent communities. You can support the project by contributing as a backer (i.e., a personal <a href="https://opencollective.com/webdrivermanager/donate">donation</a> or <a href="https://opencollective.com/webdrivermanager/contribute/backer-8132/checkout">recurring contribution</a>) or as a <a href="https://opencollective.com/webdrivermanager/contribute/sponsor-8133/checkout">sponsor</a> (i.e., a recurring contribution by a company).</p>
</div>
<h3 id="backers" class="discrete">Backers</h3>
<a href="https://opencollective.com/webdrivermanager" target="_blank"><img src="https://opencollective.com/webdrivermanager/backers.svg?width=890"></a>
<h3 id="sponsors" class="discrete">Sponsors</h3>
<a href="https://opencollective.com/webdrivermanager/sponsor/0/website" target="_blank"><img src="https://opencollective.com/webdrivermanager/sponsor/0/avatar.svg" style="width: 65px; height: 65px; object-fit: none;"></a>
<a href="https://opencollective.com/webdrivermanager/sponsor/1/website" target="_blank"><img src="https://opencollective.com/webdrivermanager/sponsor/1/avatar.svg" style="width: 65px; height: 65px; object-fit: none;"></a>
<a href="https://opencollective.com/webdrivermanager/sponsor/2/website" target="_blank"><img src="https://opencollective.com/webdrivermanager/sponsor/2/avatar.svg" style="width: 65px; height: 65px; object-fit: none;"></a>
<a href="https://opencollective.com/webdrivermanager/sponsor/3/website" target="_blank"><img src="https://opencollective.com/webdrivermanager/sponsor/3/avatar.svg"></a>
</div>
</div>
<div class="sect1">
<h2 id="further-documentation"><a class="anchor" href="#further-documentation"></a>11. Further Documentation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are other resources related to WebDriverManager and automated testing you can find helpful. For instance, the following books:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>García, Boni. <a href="https://www.oreilly.com/library/view/hands-on-selenium-webdriver/9781098109998/">Hands-On Selenium WebDriver with Java</a>. <em>O&#8217;Reilly Media, Inc.</em>, 2022.</p>
</li>
<li>
<p>García, Boni. <a href="https://www.amazon.com/Mastering-Software-Testing-JUnit-Comprehensive-ebook/dp/B076ZQCK5Q/">Mastering Software Testing with JUnit 5</a>. <em>Packt Publishing Ltd</em>, 2017.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Or the following papers:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Leotta, M., et al., "<a href="https://www.sciencedirect.com/science/article/pii/S0950584924002003">A family of experiments to quantify the benefits of adopting WebDriverManager and Selenium-Jupiter</a>." <em>Information and Software Technology</em> 178 (2025): 107595.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://link.springer.com/chapter/10.1007/978-3-031-70245-7_10">Exploring Browser Automation: A Comparative Study of Selenium, Cypress, Puppeteer, and Playwright</a>." <em>International Conference on the Quality of Information and Communications Technology</em>. Springer Nature Switzerland, 2024.</p>
</li>
<li>
<p>Leotta, M., et al. "<a href="https://dl.acm.org/doi/10.1145/3661167.3661192">AI-generated test scripts for web E2E testing with ChatGPT and Copilot: a preliminary study</a>." Proceedings of the <em>28th International Conference on Evaluation and Assessment in Software Engineering</em>. 2024.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://www.sciencedirect.com/science/article/pii/S0164121223001188">Enhancing Web Applications Observability through Instrumented Automated Browsers</a>." <em>Journal of Systems and Software</em> (2023): 111723.</p>
</li>
<li>
<p>Leotta, M., et al. "<a href="https://ieeexplore.ieee.org/document/10132210">Challenges of End-to-End Testing with Selenium WebDriver and How to Face Them: A Survey</a>". In the 16th <em>IEEE Conference on Software Testing, Verification and Validation</em> (ICST), 2023, Dublin, Ireland. April 2023.</p>
</li>
<li>
<p>Leotta, M., et al. "<a href="https://link.springer.com/chapter/10.1007/978-3-031-14179-9_3">An Empirical Study to Quantify the SetUp and Maintenance Benefits of Adopting WebDriverManager</a>." <em>15th International Conference on the Quality of Information and Communications Technology</em> (QUATIC) 2022, Talavera de la Reina, Spain. September 2022.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://www.sciencedirect.com/science/article/abs/pii/S0164121222000516">Selenium-Jupiter: A JUnit 5 extension for Selenium WebDriver</a>." <em>Journal of Systems and Software</em> (2022): 111298.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://link.springer.com/article/10.1007%2Fs10664-021-09975-3">Automated driver management for Selenium WebDriver</a>." <em>Empirical Software Engineering</em> 26.5 (2021): 1-51.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://www.mdpi.com/2079-9292/9/7/1067">A survey of the Selenium ecosystem</a>." <em>Electronics</em> 9.7 (2020): 1067.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://www.mdpi.com/2079-9292/9/3/462">Assessment of QoE for video and audio in WebRTC applications using full-reference models</a>." <em>Electronics</em> 9.3 (2020): 462.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://www.mdpi.com/2079-9292/8/8/854">Practical evaluation of VMAF perceptual video quality for WebRTC applications</a>." <em>Electronics</em> 8.8 (2019): 854.</p>
</li>
<li>
<p>García, Boni, et al. "<a href="https://ieeexplore.ieee.org/document/7992926/">WebRTC testing: challenges and practical solutions</a>." <em>IEEE Communications Standards Magazine</em> 1.2 (2017): 36-42.</p>
</li>
<li>
<p>García, Boni, and Juan Carlos Dueñas. "<a href="https://www.rintonpress.com/journals/jweonline.html#v14n56">Web browsing automation for applications quality control</a>." <em>Journal of web engineering</em> (2015): 474-502.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="about"><a class="anchor" href="#about"></a>12. About</h2>
<div class="sectionbody">
<div class="paragraph">
<p>WebDriverManager (Copyright © 2015-2025) is an open-source project created and maintained by <a href="https://bonigarcia.dev/">Boni García</a> (<a href="https://twitter.com/boni_gg">@boni_gg</a>), licensed under the terms of <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>. This documentation (also available in <a href="webdrivermanager.pdf">PDF</a>) is released under the terms of <a href="https://creativecommons.org/licenses/by-nc-sa/2.0/">CC BY-NC-SA 2.0</a>.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Version 6.3.3<br>
Updated on 08-11-2025
</div>
</div>
</body>
</html>